个性化鼠标指针

Published on
/
/趣玩前端
<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8" />
    <title>个性化鼠标指针</title>
    <style>
      body {
        margin: 0;
        height: 100vh;
        overflow: hidden;
        background: linear-gradient(#111, #000);
        display: flex;
        cursor: none;
      }

      #cursorPt {
        position: fixed;
        width: 7px;
        height: 7px;
        pointer-events: none;
        z-index: 9999;
        background: #79f2e7;
        border-radius: 50%;
        visibility: hidden;
      }

      #cursor {
        --color: #4ed5b4;
        position: fixed;
        width: 30px;
        height: 30px;
        pointer-events: none;
        z-index: 9999;
        visibility: hidden;

        background-image: linear-gradient(
            to right,
            var(--color) 10px,
            transparent 0
          ),
          linear-gradient(to bottom, var(--color) 10px, transparent 0),
          linear-gradient(to left, var(--color) 10px, transparent 0),
          linear-gradient(to bottom, var(--color) 10px, transparent 0),
          linear-gradient(to right, var(--color) 10px, transparent 0),
          linear-gradient(to top, var(--color) 10px, transparent 0),
          linear-gradient(to left, var(--color) 10px, transparent 0),
          linear-gradient(to top, var(--color) 10px, transparent 0);

        background-repeat: no-repeat;
        background-position: top left, top left, top right, top right,
          bottom left, bottom left, bottom right, bottom right;
        background-size: 20px 2px, 2px 20px, 20px 2px, 2px 20px, 20px 2px,
          2px 20px, 20px 2px, 2px 20px;
      }

      #target {
        position: relative;
        width: 150px;
        height: 80px;
        margin: auto;
        border: 2px solid #888;
        transition: 300ms;
        display: flex;
        justify-content: center;
        align-items: center;
        color: #888;
        text-transform: uppercase;
        font-family: sans-serif;
        user-select: none;
      }

      #target:hover {
        border-color: #ccc;
        color: #ddd;
      }
    </style>
  </head>
  <body>
    <fasdfasdfasdfafsdf id="cursor"></fasdfasdfasdfafsdf>
    <div id="cursorPt"></div>
    <div id="target">将鼠标悬停在此</div>
    <script src="https://unpkg.com/gsap@3/dist/gsap.min.js"></script>
    <script>
      const cursor = document.getElementById("cursor");
      const cursorPt = document.getElementById("cursorPt");
      const target = document.getElementById("target");

      const CURSOR_WIDTH = cursor.getBoundingClientRect().width;
      const CURSOR_PT_WIDTH = cursorPt.getBoundingClientRect().width;

      let isOverTarget = false;
      let rotationTween;
      let exitTween = null;
      let enterTween = null;

      function startRotation() {
        gsap.set(cursor, { rotation: 0 });
        rotationTween = gsap.to(cursor, {
          rotation: 180,
          duration: 1.2,
          repeat: -1,
          ease: "linear",
          transformOrigin: "center center",
        });
      }

      function stopRotation() {
        if (rotationTween) rotationTween.kill();
      }

      document.addEventListener("mousemove", (e) => {
        gsap.to(cursor, { autoAlpha: 1 });
        gsap.to(cursorPt, { autoAlpha: 1 });
        if (!isOverTarget) {
          gsap.to(cursor, {
            x: e.clientX - CURSOR_WIDTH / 2,
            y: e.clientY - CURSOR_WIDTH / 2,
            duration: 0.1,
            ease: "expo.out",
          });
        }
        gsap.to(cursorPt, {
          x: e.clientX - CURSOR_PT_WIDTH / 2,
          y: e.clientY - CURSOR_PT_WIDTH / 2,
          duration: 0.1,
          ease: "expo.out",
        });
      });

      target.addEventListener("mouseenter", () => {
        isOverTarget = true;
        stopRotation();

        const rect = target.getBoundingClientRect();

        if (exitTween) exitTween.kill();
        enterTween = gsap.to(cursor, {
          width: rect.width,
          height: rect.height,
          borderColor: "red",
          rotation: 360,
          duration: 0.2,
          ease: "easeOut",
        });
      });

      target.addEventListener("mousemove", (e) => {
        const rect = target.getBoundingClientRect();

        const targetWidth = rect.width;
        const targetHeight = rect.height;

        const cx = rect.left + targetWidth / 2;
        const cy = rect.top + targetHeight / 2;

        const dx = e.clientX - cx;
        const dy = e.clientY - cy;

        gsap.to(cursor, {
          x: rect.left + dx * 0.09,
          y: rect.top + dy * 0.09,
          scale: 1.1,
          duration: 0.1,
          ease: "power2.out",
        });
      });

      target.addEventListener("mouseleave", () => {
        isOverTarget = false;

        exitTween = gsap.to(cursor, {
          width: 30,
          height: 30,
          duration: 0.5,
          ease: "elastic.out(1, .9)",
        });

        startRotation();
      });

      startRotation();
    </script>
  </body>
</html>