You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ReactJs中使用MouseMove事件实现自定义光标交互样式问题

React自定义光标悬停字符放大效果修复方案

核心问题点

  • mousemove事件绑定错误:事件绑定在设置了pointer-events:none的光标元素上,元素无法接收鼠标事件,导致光标位置不更新
  • 初始状态未做隐藏处理:鼠标位置初始值为(0,0),页面加载后光标直接显示在左上角
  • CSS兄弟选择器兼容性差:~选择器强依赖DOM同级位置,只要DOM层级调整就会失效,且无法保证hover状态准确触发
  • transform属性覆盖问题:悬停时直接写transform:scale(6)会覆盖原有的translate(-50%,-50%)居中偏移,导致放大时光标位置错位

修复后完整实现

组件代码

import { useState, useEffect, useRef } from 'react';
// 若使用animate.css需提前引入依赖
// import 'animate.css';

export default function CustomCursor() {
  const [mousePosition, setMousePosition] = useState({ left: 0, top: 0 });
  // 控制光标初始显示状态,避免加载时显示在左上角
  const [cursorVisible, setCursorVisible] = useState(false);
  // 标记光标是否悬停在字母Z上
  const [hoverOnZ, setHoverOnZ] = useState(false);
  const letterZRef = useRef(null);

  useEffect(() => {
    const handleMouseMove = (e) => {
      setMousePosition({ left: e.pageX, top: e.pageY });
      
      // 首次检测到鼠标移动后显示光标
      if (!cursorVisible) setCursorVisible(true);

      // 碰撞检测判断鼠标是否落在字母Z范围内
      if (letterZRef.current) {
        const zRect = letterZRef.current.getBoundingClientRect();
        const inRange = 
          e.clientX >= zRect.left &&
          e.clientX <= zRect.right &&
          e.clientY >= zRect.top &&
          e.clientY <= zRect.bottom;
        setHoverOnZ(inRange);
      }
    };

    // 全局绑定鼠标移动事件,不依赖光标元素接收事件
    window.addEventListener('mousemove', handleMouseMove);
    return () => window.removeEventListener('mousemove', handleMouseMove);
  }, [cursorVisible]);

  return (
    <div className="page-container">
      {/* 页面其他内容 */}
      <span
        ref={letterZRef}
        className="letterZ animate__animated animate__zoomIn delay1"
      >
        Z
      </span>

      {/* 自定义光标 */}
      <div
        className={`cursor ${hoverOnZ ? 'cursor-hover' : ''}`}
        style={{
          left: mousePosition.left,
          top: mousePosition.top,
          opacity: cursorVisible ? 1 : 0
        }}
      />
    </div>
  );
}

样式代码

/* 全局隐藏系统默认光标,避免双光标 */
* {
  cursor: none;
}

.cursor {
  position: fixed;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  /* 过渡仅作用于需要动画的属性,性能更好 */
  transition: transform 0.1s ease, opacity 0.2s ease;
  /* 保留居中偏移 */
  transform: translate(-50%, -50%);
  pointer-events: none;
  mix-blend-mode: difference;
  /* 保证光标始终在最上层 */
  z-index: 9999;
}

/* 悬停字母Z时的放大状态,保留居中偏移避免错位 */
.cursor.cursor-hover {
  transform: translate(-50%, -50%) scale(6);
}

关键优化说明

  • 事件绑定到window全局,不受光标元素pointer-events:none属性影响,保证位置实时更新
  • 初始状态设置光标透明,首次检测到鼠标移动后再显示,彻底解决加载时光标固定在左上角的问题
  • 用ref+元素边界碰撞检测代替CSS兄弟选择器判断hover状态,不依赖DOM结构,稳定性更高
  • 悬停放大时保留居中偏移属性,避免缩放时光标位置错位
  • 全局隐藏系统默认光标,避免自定义光标和系统光标同时显示的问题

内容的提问来源于stack exchange,提问作者Zakaria Belassal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 17:42:25