repos

isaacbythewood.com-nextjs

mirror archived upstream

Animation-heavy personal portfolio on Next.js: a custom cursor, a page-transition loader, and canvas backgrounds.

canvas-animationscss-modulesdark-themehandcodednextjspersonal-websiteportfolioreact

2.0 KB · 76 lines · JavaScript Raw History
 1import React, { useEffect, useRef } from "react";
 2import styles from "@styles/components/mouse.module.css";
 3
 4const Mouse = () => {
 5  const cursorRef = useRef(null);
 6  const circleRef = useRef(null);
 7  const mousePos = useRef({ x: 0, y: 0 });
 8
 9  const isInteractiveRef = useRef(false);
10
11  useEffect(() => {
12    const onMouseMove = (e) => {
13      mousePos.current = { x: e.clientX, y: e.clientY };
14    };
15
16    const onMouseOver = (e) => {
17      const target = e.target;
18      isInteractiveRef.current =
19        target.tagName === "BUTTON" ||
20        target.tagName === "A" ||
21        !!target.closest("a, button") ||
22        (target.classList && target.classList.contains("mouse-activate"));
23      if (circleRef.current) {
24        circleRef.current.classList.toggle(
25          "activated",
26          isInteractiveRef.current
27        );
28      }
29    };
30
31    document.addEventListener("mousemove", onMouseMove, { passive: true });
32    document.addEventListener("mouseover", onMouseOver, { passive: true });
33
34    let animationFrameId;
35
36    const animate = () => {
37      const { x, y } = mousePos.current;
38
39      if (cursorRef.current)
40        cursorRef.current.style.transform = `translate3d(${x}px, ${y}px, 0)`;
41      if (circleRef.current)
42        circleRef.current.style.transform = `translate3d(${x}px, ${y}px, 0)`;
43
44      animationFrameId = requestAnimationFrame(animate);
45    };
46
47    animate();
48
49    return () => {
50      document.removeEventListener("mousemove", onMouseMove);
51      document.removeEventListener("mouseover", onMouseOver);
52      cancelAnimationFrame(animationFrameId);
53    };
54  }, []);
55
56  return (
57    <>
58      <div ref={cursorRef} className={styles.pageCursor}>
59        <svg
60          width="16"
61          height="16"
62          viewBox="0 0 16 16"
63          fill="#ff2d2d"
64          fillOpacity="0.9"
65          xmlns="http://www.w3.org/2000/svg"
66        >
67          <circle cx="8" cy="8" r="8" />
68        </svg>
69      </div>
70      <div ref={circleRef} className={styles.pageCursorCircle} />
71    </>
72  );
73};
74
75export default Mouse;