React自定义光标渲染两个div:一个随动一个固定左上角
解决React自定义光标出现重复元素的问题
你的问题核心在于未清理旧DOM元素 + useEffect依赖触发重复执行:
- 当
pencilSize变化时,useEffect会重新执行,每次都会调用mouseCursor()创建新的.cursordiv并添加到body,但之前的旧光标没被删除,导致页面积累多个光标元素。 - 事件处理函数只操作第一个
.cursor元素,新创建的光标没人控制,就会固定在视口左上角。
修复步骤
- 在useEffect清理阶段删除旧光标:每次useEffect重新执行前,移除之前创建的光标元素,避免重复创建。
- 用ref保存光标引用:不用
getElementsByClassName查找元素,直接通过ref操作当前光标,避免取错对象。 - 用useCallback包裹事件函数:保证函数实例稳定,避免组件重渲染时频繁解绑/绑定事件。
修改后的完整代码
import { useRef, useEffect, useState, useCallback } from "react" function Pcanvas({width, height, pixelSize}){ const [canvasColor, setCanvasColor] = useState("#dcdcdc") const [pencilSize, setPencilSize] = useState(pixelSize) const canvasRef = useRef(null) const cursorRef = useRef(null) // 保存当前光标元素的引用 var mousePressed = false function mouseCursor(){ const cursor = document.createElement("div") cursor.className = 'cursor' cursor.style.backgroundColor = 'black' cursor.style.width = `${pencilSize}px` cursor.style.height = `${pencilSize}px` cursor.style.position = 'absolute'; cursor.style.display = 'block'; cursor.style.zIndex = 999; cursor.style.pointerEvents = 'none'; document.body.appendChild(cursor) cursorRef.current = cursor // 将光标存入ref return cursor } // 用useCallback包裹,依赖pencilSize确保尺寸变化时函数更新 const handleMouseMov = useCallback((event) => { const cursor = cursorRef.current if(cursor){ cursor.style.top = `${event.clientY - pencilSize/2}px` cursor.style.left = `${event.clientX - pencilSize/2}px` } }, [pencilSize]) const handleMouseLeave = useCallback(() => { const cursor = cursorRef.current if(cursor){ cursor.style.display = 'none' } }, []) const handleMouseEnter = useCallback(() => { const cursor = cursorRef.current if(cursor){ cursor.style.display = 'block' } }, []) useEffect(() => { const cursor = mouseCursor() document.body.addEventListener('mouseenter', handleMouseEnter) document.body.addEventListener('mousemove', handleMouseMov) document.body.addEventListener('mouseleave', handleMouseLeave) return(() => { // 清理阶段:移除事件监听 + 删除旧光标 document.body.removeEventListener('mouseenter', handleMouseEnter) document.body.removeEventListener('mousemove', handleMouseMov) document.body.removeEventListener('mouseleave', handleMouseLeave) if(cursor) { document.body.removeChild(cursor) cursorRef.current = null } }) }, [pencilSize, handleMouseEnter, handleMouseMov, handleMouseLeave]) // 加入事件函数依赖确保监听正确 return( <></> ) } export default Pcanvas
内容的提问来源于stack exchange,提问作者Ahsan914
相关产品推荐
相关产品推荐

