React Hooks⚡️를 사용한 멋진 애니메이션 커서

내장된 커서가 좀 지루하지 않나요?🥱 저도요. 그래서 나는 내 자신을 만들었습니다.


커서에 기본 스타일과 논리를 추가하여 시작하겠습니다.

.cursor {
  width: 40px;
  height: 40px;
  border: 2px solid #fefefe;
  border-radius: 100%;
  position: fixed;
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 9999;
  mix-blend-mode: difference;
}

html, body {
  cursor: none;
  background-color: #121212;
}



const Cursor = () => {
    return <div className="cursor"/>
}

ReactDOM.render(
    <div className="App">
        <Cursor/>
    </div>,
    document.getElementById('root')
);

이제 마우스 이동에 따라 커서의 위치를 ​​변경하려고 합니다.

const Cursor = () => {
+   const [position, setPosition] = useState({x: 0, y: 0});
+
+   useEffect(() => {
+       addEventListeners();
+       return () => removeEventListeners();
+   }, []);
+
+   const addEventListeners = () => {
+       document.addEventListener("mousemove", onMouseMove);
+   };
+
+   const removeEventListeners = () => {
+       document.removeEventListener("mousemove", onMouseMove);
+   };
+
+   const onMouseMove = (e) => {
+       setPosition({x: e.clientX, y: e.clientY});
+   };                                                               
+
-   return <div className="cursor"/>
+   return <div className="cursor"
+           style={{
+               left: `${position.x}px`,
+               top: `${position.y}px`
+           }}/>
}

...

구성 요소가 마운트되면 이벤트를 처리하는 이벤트 리스너를 추가하고mousemove 구성 요소가 마운트 해제될 때 제거합니다. onMouseMove 함수에서 e.clientXe.clientY 속성을 기반으로 새 커서의 위치를 ​​설정합니다.



이제 커서가 마우스 움직임에 반응하지만 보시다시피 마우스가 화면을 떠날 때 커서가 숨겨지지 않습니다. 그래서 그것을 고치자!

.cursor {
  ...
+ transition: all 150ms ease;
+ transition-property: opacity;
}

+ .cursor--hidden {
+   opacity: 0;
+ }

...



+ import classNames from "classnames";

const Cursor = () => {
    const [position, setPosition] = useState({x: 0, y: 0});
+   const [hidden, setHidden] = useState(false);

...

    const addEventListeners = () => {
        document.addEventListener("mousemove", onMouseMove);
+       document.addEventListener("mouseenter", onMouseEnter);
+       document.addEventListener("mouseleave", onMouseLeave);
    };

    const removeEventListeners = () => {
        document.removeEventListener("mousemove", onMouseMove);
+       document.removeEventListener("mouseenter", onMouseEnter);
+       document.removeEventListener("mouseleave", onMouseLeave);
    };
+
+   const onMouseLeave = () => {
+       setHidden(true);
+   };
+
+   const onMouseEnter = () => {
+       setHidden(false);
+   };
    ...
+
+   const cursorClasses = classNames(
+       'cursor',
+       {
+           'cursor--hidden': hidden
+       }
+   );                                                             
+
-   return <div className="cursor"
+   return <div className={cursorClasses}
            style={{
                left: `${position.x}px`,
                top: `${position.y}px`
            }}/>
}

...

그래서 mouseleavemouseenter 처리기를 추가합니다. 마우스가 화면에 들어올 때 opacity1 가 되고 떠날 때 - 는 0 가 됩니다. 또한 classNames를 조건부로 결합하기 위한 간단한 유틸리티인 라이브러리classnames를 추가합니다.



이제 훨씬 좋아 보이지만 더 많은 것을 추가해 봅시다!

클릭 애니메이션을 추가해 보겠습니다.

.cursor {
  ...
- transition-property: opacity;
+ transition-property:  opacity, background-color, transform, mix-blend-mode;
  ...
}

+ .cursor--clicked {
+   transform: translate(-50%, -50%) scale(0.9);
+   background-color: #fefefe;
+ }

...



const Cursor = () => {
    ...
+   const [clicked, setClicked] = useState(false);

    const addEventListeners = () => {
        ...
+       document.addEventListener("mousedown", onMouseDown);
+       document.addEventListener("mouseup", onMouseUp);
    };

    const removeEventListeners = () => {
        ...
+       document.removeEventListener("mousedown", onMouseDown);
+       document.removeEventListener("mouseup", onMouseUp);
    };
+
+   const onMouseDown = () => {
+       setClicked(true);
+   };
+
+   const onMouseUp = () => {
+       setClicked(false);
+   };

    ...

    const cursorClasses = classNames(
        'cursor',
        {
+           'cursor--clicked': clicked,
            'cursor--hidden': hidden
        }
    );

...

마우스 클릭은 mousedownmouseup 이벤트에 의해 처리됩니다. 마우스를 클릭하면 커서의 눈금이 0.9 로, 배경이 #fefefe 로 변경됩니다.



마지막 애니메이션으로 넘어갑시다!

이제 링크가 떠 있을 때 몇 가지 효과를 추가합니다.

...

+ .cursor--link-hovered {
+   transform: translate(-50%, -50%) scale(1.25);
+   background-color: #fefefe;
+ }
+
+ a {
+   text-decoration: underline;
+   color: #fefefe;
+ }

...



const Cursor = () => {
    ...
+   const [linkHovered, setLinkHovered] = useState(false);

    useEffect(() => {
       addEventListeners();
+      handleLinkHoverEvents();
       return () => removeEventListeners();
    }, []);
+   
    ...
+
+   const handleLinkHoverEvents = () => {
+       document.querySelectorAll("a").forEach(el => {
+           el.addEventListener("mouseover", () => setLinkHovered(true));
+           el.addEventListener("mouseout", () => setLinkHovered(false));
+       });
+   };

    const cursorClasses = classNames(
        'cursor',
        {
            'cursor--clicked': clicked,
            'cursor--hidden': hidden,
+           'cursor--link-hovered': linkHovered
        }
    );
    ...
}

ReactDOM.render(
    <div className="App">
+       <a>This is a link</a>
        <Cursor/>
    </div>,
    document.getElementById('root')
);


구성 요소가 마운트되면 handleLinkHoverEvents 모든 링크 요소에 이벤트 수신기를 추가합니다. 링크가 가리키면 cursor--link-hovered 클래스가 추가됩니다.



마지막 단계에서는 모바일/터치 장치에서 렌더링<Cursor/>하지 않습니다.

+ const isMobile = () => {
+     const ua = navigator.userAgent;
+     return /Android|Mobi/i.test(ua);
+ };

const Cursor = () => {
+   if (typeof navigator !== 'undefined' && isMobile()) return null;
    ...
}

...

그리고 끝났습니다! 전체 코드펜 예제는 다음과 같습니다.




사용자 지정 커서 애니메이션을 추가하는 것은 생각보다 어렵지 않습니다. 이 문서가 자신의 커서를 사용자 지정하기 위해 수행할 수 있는 작업에 대한 기본 아이디어를 제공하기를 바랍니다.

읽어 주셔서 감사합니다!

좋은 웹페이지 즐겨찾기