Interactive한 포트폴리오를 위한 - 마우스커서 애니메이션 만들기(쉬움) HTML,CSS,JS

Ina·2020년 9월 18일
151
post-thumbnail

제 개인 포트폴리오 웹사이트(만들다가 잠정 중단된,, 😅 )에 사용된 애니메이션 효과중에서 주위에서 반응이 좋았던 마우스커서 애니메이션을 소개해드리고자 합니다!

코드도 짧고 난이도도 쉬운 편이니 한번 도전해보세요!

Reference

DevEd - Awesome Cursor Animation With CSS & Javascript!

저는 DevEd 에드쌤 코드를 커스터마이징(dashed line 추가, 스크롤 대응)해서 제 포트폴리오에 적용해봤습니다. 여러분도 한번 따라해보시고 응용해서 포트폴리오 웹사이트에 적용해보세요 😃

📍 HTML

우선 커서의 스타일링을 용이하게 하기 위해! 시스템 커서 대신 div태그로 (가짜) 커서를 따로 만들어주겠습니다. (뒤에서 CSS로 시스템 마우스커서는 안 보이도록 숨김 처리할 예정입니다)

시스템 마우스커서를 대신할 div 커서 만들기

<header>
  <div class="cursor"></div>
  <nav>
  마우스오버했을 때 스타일이 바뀌게 될 네비게이션 메뉴 ⬇
    <ul class="gnb">
      <li><a href="index.html" title="home" class="pink">Home</a></li>
      <li><a href="work.html" title="work">Work</a></li>
      <li><a href="about.html">About</a></li>
      <li><a href="playground.html">Playground</a></li>
    </ul>
 </nav>
  ...
  ...여러분의 포트폴리오 html코드...
  ...
</header>

나중에 .cursor의 위치를 JS로 조절해줄 예정이라, div태그를 본문 어디에 넣는지는 크게 상관 없지만 저는 눈에 잘 띄도록 header 태그 바로 안쪽에 넣어주었습니다.


📍 CSS

1) .cursor 기본 스타일 정의

  1. 포트폴리오 메인/서브 색상은 변수에 담아 사용해줬습니다.(필수 아님)
  2. cursor: none 속성으로 시스템 커서를 숨겨줍니다
  3. 커스텀 커서(가짜 커서) 스타일을 정의해줍니다.
    • 뒤에서 JS로 .cursor의 top, left 값을 조절해야 하기 때문에 position 속성 정의는 필수입니다!
    • 커스텀 커서가 실제 커서 위치의 중심에 딱 들어맞도록 transform: translate(-50%, -50%) 속성을 넣어줍니다.
    • 자연스러운 애니메이션 효과도 넣어주시고요!
    • backdrop-filter는 element 아래에 있는 요소에 필터가 씌워진 듯한 효과를 내는 속성입니다.
<style>
/* 기본 색상 변수에 담기 */
  html {
      --color-texta: #1d1d1d;
      --color-textb: #fff;
  }
/* 시스템 커서 안 보이게 숨기기 */
  body {
      cursor: none;
  }
  /* 커스텀 커서 스타일 정의해주기 */
  .cursor {
      width: 3rem;
      height: 3rem;
      border: 2px solid var(--color-texta);
      border-radius: 50%;
      position: absolute;
      z-index: 1000;
      transform: translate(-50%, -50%);
      pointer-events: none;
      transition: all 0.3s ease;
      transition-property: background, transform;
      transform-origin: 100% 100%;
      backdrop-filter: sepia(20%);
      background-size: cover;
  }
</style>

여기까지 해주시면 아래 사진과 같이 우리의 커스텀 커서가 화면 저 왼쪽 구석에 박혀있는 모습을 확인하실 수 있습니다. (나중에 JS 코드 몇 줄만 추가하면 진짜 커서처럼 움직일테니 조금만 참아주세요~!)

2) 메뉴 위에 마우스오버됐을 때 .cursor 스타일 정의

메뉴(또는 원하는 요소)에 커서를 댔을 때 바뀔 커서의 스타일을 .cursor-grow 클래스에 정의해줍니다.
저는 transform: scale() 속성으로 커스텀 커서의 크기를 키우고, 배경색은 어둡게 해주었습니다.
(위에 캡쳐는 스타일만 보여드리기 위한 예시이고, 아직 커서는 움직이지 않아요!)

마우스 오버됐을 때 커스텀 커서 스타일

<style>
.cursor-grow {
    transform: scale(2);
    background-color: var(--color-texta);
}
</style>

3) 마우스오버된 "메뉴"의 스타일 정의

마우스 커서만 바뀌면 쪼오금 심심할 것 같아서 저는 hover된 메뉴에도 점선 스타일을 추가해봤습니다.

제가 사용한 방법은,
먼저 dashed line(점선)을 만들기 위해서 위와 같은 초소형 동그라미 ⚪️ svg 파일을 img 폴더에 저장해놓고 ➡
메뉴(a tag)에 ::after요소를 추가하여 backgroun-image를 동그라미 ⚪️ svg 파일로 설정해주었습니다.

<style>
/* 점선이 들어갈 ::after(pseudo)요소 만들고 크기값 설정해주기*/
  .gnb li a:hover::after {
    width: calc(100% - 2rem);
    height: 3px;
    background-size: contain; /* 배경 이미지(svg)가 알맞게 들어가도록*/
  }
/* hover된 링크의 글자 색상 정의(하얀색)*/
/* .hovered-link 선택자명은 추후 JS로 추가할 예정!*/
  .hovered-link {
      color: var(--color-textb) !important;
  }
/* .gnb li a:hover::after의 svg에 배경이미지 지정!*/
  .hovered-link::after {
      background-image: url("../img/white_dot.svg");
  }
</style>

📌 배경이미지가 ⚪️ 동그라미 하나인데 어떻게 dashed-line 형태가 되죠⁉️

background-image 속성을 정의하면 background-repeat : repeat 속성도 디폴트로 적용됩니다. 배경이미지가 적용된 요소의 크기를 꽉 채울 때까지 repeat 되므로 초소형 동그라미가 반복되면서 점선처럼 보이게 됩니다. (제가 사용한 동그라미 svg 이미지 코드는 아래와 같습니다)

  <svg width="4" height="2" viewBox="0 0 4 2" fill="none" xmlns="http://www.w3.org/2000/svg">
      <circle cx="1" cy="1" r="1" fill="#ffffff"/>
  </svg>

📍 Javascript

1) .cursor를 마우스 커서로 바꿔치기

드디어 커스텀 커서가 진짜 커서처럼 동작하도록 JS 코드를 추가해보겠습니다.

커스텀 커서가 실제 커서의 위치에 딱 붙어서 움직이게 하기 위해서

  • mousemove 이벤트를 window에 추가해줍니다
  • 그리고 마우스가 움직일 때마다 실제 커서의 xy 좌표값(pageX, pageY) 를 읽어내고 커스텀 커서의 left, top 속성에 넣어줍니다.(이 단계를 위해서 .cursor에 position을 정의해주었지요!)

📌 커서의 top값은 왜 (e.pageY - scrollY) 인가요??

보통 좌우 스크롤이 생기는 경우는 거의 없기에 pageX값은 위치값에 그대로 적용이 가능하지만, 상하 스크롤이 생기는 페이지의 경우, Y좌표값만 적용시 스크롤다운 했을 때 커서가 위로 밀리는 현상이 발생합니다. 그래서 scroll된 값도 함께 고려해주어야 실제 커서와 동일한 위치에 커스텀 커서를 위치시킬 수 있습니다.

(별 것 아닌 것 같지만 저는 정말 한참 고민해서 구현했답니다ㅠㅠ)

<script>
  let mouseCursor = document.querySelector(".cursor");
  let navLinks = document.querySelectorAll(".gnb li a"); //메뉴 링크
  //window 객체에 scroll & mouse 이벤트를 추가하고 cursor함수 실행되도록 함
  window.addEventListener("scroll", cursor);
  window.addEventListener("mousemove", cursor);
  //커스텀 커서의 left값과 top값을 커서의 XY좌표값과 일치시킴
  function cursor(e) {
    mouseCursor.style.left = e.pageX + "px";
    mouseCursor.style.top = e.pageY - scrollY + "px";
}
</script>

2) 메뉴에 마우스오버됐을 때 스타일 적용하기

드디어 마지막 단계입니다!
앞에서 정의해두었던 CSS 스타일을 실제로 적용하기만 하면 끝~~

네비게이션 메뉴에 각각 mouseover 이벤트가 발생했을 시,

  • 커스텀 커서에 .link-grow 선택자를 추가하고, (커서 크기가 커지고 색상이 변하는 스타일이 정의되어 있음)
  • z-index를 -1로 주어 커서가 메뉴 아래에 위치하도록 조절하고,
  • 각 메뉴에 .hovered-link 선택자를 추가해줍니다. (글자 색상 변하고 점선이 생기는 스타일이 정의되어 있음)
  • mouseleave시에는 반대로 추가된 선택자들을 빼주고 z-index도 원상복귀 시켜주면 되겠죠?
<script>
  navLinks.forEach((link) => {
    link.addEventListener("mouseover", () => {
      mouseCursor.classList.add("link-grow");
      mouseCursor.style.zIndex = "-1";
      link.classList.add("hovered-link");
    });
    link.addEventListener("mouseleave", () => {
      mouseCursor.classList.remove("link-grow");
      mouseCursor.style.zIndex = "1000";
      link.classList.remove("hovered-link");
    });
  });
</script>

네 여기까지 따라오시느라 수고 많으셨어요!
이제 여러분의 미적감각을 발휘해서 더~~~ 멋진 애니메이션을 포트폴리오에 한번 넣어보시길 바랍니당 🤞

(제 포스팅이 도움이 되셨다면 좋아요나 댓글 꼬옥 부탁드려요!!!! 여러분의 관심은 더 양질의 포스팅을 쓰는 원동력이 됩니다😃)

profile
프론트엔드 개발자. 기록하기, 요가, 등산

12개의 댓글

comment-user-thumbnail
2020년 10월 8일

감사합니다! 덕분에 멋진 포트폴리오 만들 수 있을것 같아요!

1개의 답글
comment-user-thumbnail
2020년 10월 13일

좋은 정보 감사합니다 유용하게 예쁘게 써보도록할게요♡

1개의 답글
comment-user-thumbnail
2021년 3월 22일

좋은 정보 알려주셔서 감사합니다!ㅎㅎ

답글 달기
comment-user-thumbnail
2021년 6월 11일

크롬,엣지에서는 정상작동 하는데 익스플로러에서는 동작을 안하네요 혹시 해결방법 있을까요?

1개의 답글
comment-user-thumbnail
2021년 8월 3일

감사합니다 ㅠㅠㅠㅠㅠ

답글 달기
comment-user-thumbnail
2022년 2월 7일

안녕하세요.
서치 중 우연히 방문했습니다.
지금 소개해주신 기술은 제가 특허낸 기술입니다.
https://blog.naver.com/byeong3696/222571624732
참고 부탁드릴게요.

답글 달기
comment-user-thumbnail
2022년 5월 5일

Good ~ ★

답글 달기
comment-user-thumbnail
2023년 2월 6일

혹시

navLinks.forEach((link) => {
link.addEventListener("mouseover", () => {
mouseCursor.classList.add("link-grow");
mouseCursor.style.zIndex = "-1";
link.classList.add("hovered-link");
});
.. (생략)...
});

이 부분에서 "link-grow"가 아니라 "cursor-grow"가 맞지 않나요~? 클래스명이 달라서 적용이 안되는 것 같아서요!

답글 달기