React useEffect中添加的Fabric JS对象mouseover事件无法触发
React中Fabric.js动态添加对象无法触发mouse:over事件的解决方案
问题现象
使用React的useEffect初始化Fabric.js画布后,立即添加的矩形能正常响应mouse:over事件,但通过setTimeout或状态变化动态添加的矩形,悬停时无法触发该事件(控制台打印不出目标对象)。
初始化核心代码如下:
useEffect(() => { canvasRef.current = new fabric.Canvas("c", { width: 300, height: 600, backgroundColor: "#666", selection: false }); canvasRef.current.add( new fabric.Rect({ width: 100, height: 100, top: 100, left: 100, fill: "#660000" }) ); canvasRef.current.on("mouse:over", function (e) { console.log("mouse over", e.e.clientX, e.e.clientY, e.target); }); canvasRef.current.requestRenderAll(); setTimeout(() => { canvasRef.current.add( new fabric.Rect({ width: 100, height: 100, top: 200, left: 100, fill: "#666600" }) ); canvasRef.current.requestRenderAll(); }, 1000); }, []);
原因分析
Fabric.js的mouse:over事件依赖对象的hoverCursor属性(默认值为'pointer'),但React严格模式会干扰Fabric.js内部的鼠标事件检测逻辑——动态添加的对象未能正确注册鼠标交互所需的属性,导致全局画布事件无法识别新对象。此外,异步添加对象时,若画布事件上下文的绑定逻辑未同步更新,也会引发该问题。
解决方法
方法1:禁用React严格模式
在项目入口文件(如index.js)中移除<React.StrictMode>的包裹。严格模式会触发两次渲染,打乱Fabric.js的事件初始化流程,禁用后动态添加的对象即可正常响应事件。
方法2:显式设置对象的hoverCursor属性
动态添加对象时,强制指定hoverCursor属性,确保Fabric.js将其标记为可交互元素:
setTimeout(() => { canvasRef.current.add( new fabric.Rect({ width: 100, height: 100, top: 200, left: 100, fill: "#666600", hoverCursor: 'pointer' // 显式声明交互光标 }) ); canvasRef.current.requestRenderAll(); }, 1000);
方法3:重新绑定画布事件(兜底方案)
若前两种方法无效,可在每次添加新对象后,先解绑旧事件再重新绑定,确保事件监听覆盖所有对象:
const handleMouseOver = (e) => { console.log("mouse over", e.e.clientX, e.e.clientY, e.target); }; useEffect(() => { const canvas = canvasRef.current = new fabric.Canvas("c", { width: 300, height: 600, backgroundColor: "#666", selection: false }); canvas.add( new fabric.Rect({ width: 100, height: 100, top: 100, left: 100, fill: "#660000" }) ); canvas.on("mouse:over", handleMouseOver); canvas.requestRenderAll(); setTimeout(() => { canvas.add( new fabric.Rect({ width: 100, height: 100, top: 200, left: 100, fill: "#666600" }) ); // 先解绑再重新绑定,确保事件覆盖新对象 canvas.off("mouse:over", handleMouseOver); canvas.on("mouse:over", handleMouseOver); canvas.requestRenderAll(); }, 1000); // 组件卸载时清理事件与画布 return () => { canvas.off("mouse:over", handleMouseOver); canvas.dispose(); }; }, []);
验证要点
- 动态添加对象后必须调用
requestRenderAll()刷新画布 - 检查Fabric.js版本,旧版本可能存在事件委托bug,建议升级至最新稳定版
内容的提问来源于stack exchange,提问作者Cristian
相关产品推荐
相关产品推荐

