마우스커서 따라다니기

마우스를 부드럽게 따라다니는 커스텀 커서

awdagency.com 사이트에서 커서 모션 애니메이션이 눈에 띄어 따라 만들어봤습니다.

커스텀 마우스 커서를 만들기 위해서 먼저 css 로 가려줍니다.

html {
  cursor: none;
}

그다음 커스텀 마우스 커서를 지정할 태그를 생성합니다. 주된 커스텀 마우스 커서 외에 뒤에서 따라다니는 원은 부드러운 모션을 주기 위해 따로 빼둡니다.

<body>
  <div class="cursor-wrapper">
    <!-- 마우스 위치를 감지하고 위치를 변경하기 위한 wrapper -->
    <div class="custom-cursor"></div>
    <!--마우스 커서 모양 -->
    <div class="cursor-text">DISCOVER</div>
    <!-- 커서가 이미지 위에서 있을 때 표시해줄 텍스트-->
  </div>
  <!-- 마우스 커서보다 한 템포 느리게 뒤따라 오는 꼬리-->
  <div class="cursor-tail"></div>
</body>

커스텀 마우스 커서의 positionabsolute로 지정합니다. topleft 값을 건드려 움직여줄 겁니다.

.cursor-wrapper {
  position: absolute;
  top: 0;
  left: 0;
  width: 25px;
  height: 25px;
  border-radius: 50%;
}

커스텀 커서 클래스를 만듭니다.

class CustomCursor {
  constructor({ $target }) {
    this._$target = $target // custom cursor wrapper 태그
    this._radius = $target.clientHeight / 2 // 반지름
    this._pos = {
      x: null,
      y: null,
    } // 마우스 위치
  }

  // 변경된 마우스 위치를 전달 받는 접근자 프로퍼티
  set pos(newPos) {
    this._pos = newPos
    // 정확히 마우스 위치가 중앙으로 오도록 반지름을 빼줌
    this._$target.style.top = `${newPos.y - this.radius}px`
    this._$target.style.left = `${newPos.x - this.radius}px`
  }
}

mousemove 이벤트에서 마우스 위치를 가져와 customCursor의 위치를 동기화합니다.

const customCursor = new CustomCursor({
  $target: document.querySelector('.cursor-wrapper'),
})

window.addEventListener('mousemove', e => {
  customCursor.pos = { x: e.x, y: e.y }
})

이제 마우스 뒤를 쫒아오는 꼬리를 만져줍니다. 이부분은 마우스 부드럽게 따라오는 이미지 글을 참고했습니다. cursor-tail의 위치 변경을 mousemove 이벤트 콜백함수에 같이 두지 않은 까닭은 마우스가 멈춰도 끝까지 따라가 마우스가 있는 위치에 정확히 옮겨주기 위함입니다.

const $cursorTail = document.querySelector('.cursor-tail')

function followCursor() {
  // 현재 꼬리 위치 가져오기
  const tail_x = parseInt($cursorTail.style.left.replace('px', '')) || 0
  const tail_y = parseInt($cursorTail.style.top.replace('px', '')) || 0

  // 커서와 꼬리 사이의 거리를 나누는 값 (10) 으로 속도 조절을 합니다.
  // 나누는 값이 클수록 느려지고 작을수록 빨라집니다
  $cursorTail.style.top = `${Math.round(
    tail_y + (customCursor.pos.y - tail_y) / 10
  )}px`
  $cursorTail.style.left = `${Math.round(
    tail_x + (customCursor.pos.x - tail_x) / 10
  )}px`
  requestAnimationFrame(followCursor)
}

requestAnimationFrame(followCursor)

저처럼 수학공식이 이해하기 어려운 분들을 위해서 제가 이해한 데까지 설명하자면 이렇습니다.

꼬리 x축 위치를 곧바로 customerCusor의 x축 위치에 놓게 되면 꼬리와 커서가 동시에 움직이게 되어 원하는 ‘따라가는 모션’을 보여줄 수 없습니다.

$cursorTail.style.left = `${customCursor.pos.x}px`

현재 꼬리의 x축 위치에서 커서와 x축으로 떨어진 거리를 더해줘도 위와 동일한 결과가 나옵니다.

$cursorTail.style.left = `${tail_x + (customCursor.pos.x - tail_x)}px`

// = tail_x - tail_x + customCursor.pos.x
// = 0 + customCursor.pos.x
// = customCursor.pos.x

현재 꼬리의 x축과 커서의 x축 위치의 거리를 일정한 값(여기선 10)으로 나누면 어떻게 될까요? 꼬리의 x축 위치가 이전보다는 곧바로 customerCursor의 x축 위치로 가지 않겠죠?

$cursorTail.style.left = `${Math.round(
  tail_x + (customCursor.pos.x - tail_x) / 10
)}px`

tail_x(꼬리 x축 위치) 가 customCursor.pos.x(커서 x축 위치) 값에 가까워지면 (customCursor.pos.x - tail_x) / 10 이 0에 수렴되어 $cursorTail.style.left = tail_x로 정지상태가 됩니다. 즉, 커서 위치에서 멈추게 됩니다.

참고 사이트

마우스 부드럽게 따라오는 이미지
awdagency.com


Written by@dodo
프론트엔드 개발자 도도입니다. 일상, 개발, 독서 등을 기록합니다.

GitHubFacebookLinkedIn