React画布矩形绘制异常:悬停触发绘制,需实现点击绘制+移出消失
问题解决:仅鼠标点击拖拽时绘制矩形,移出范围自动消失
问题根源
stopDrawingRectangle中注释了setIsDrawing(false),导致isDrawing状态始终为true,鼠标移动就会触发绘制- 缺少鼠标移出矩形范围时的检测与画布清空逻辑
- 事件绑定范围与时机不够精准
修复后的完整代码
import { useState, useRef, useEffect } from 'react'; const CanvasDrawer = () => { const [isDrawing, setIsDrawing] = useState(false); const canvasRef = useRef(null); const videoRef = useRef(null); const startX = useRef(null); const startY = useRef(null); const canvasOffSetX = useRef(0); // 初始化偏移量 const canvasOffSetY = useRef(0); const currentRect = useRef(null); // 存储当前绘制的矩形信息 const contextRef = useRef(null); useEffect(() => { const canvas = canvasRef.current; if (!canvas || !videoRef.current) return; // 初始化canvas尺寸和偏移量 canvas.width = videoRef.current.clientWidth; canvas.height = videoRef.current.clientHeight; const rect = canvas.getBoundingClientRect(); canvasOffSetX.current = rect.left; canvasOffSetY.current = rect.top; contextRef.current = canvas.getContext("2d"); const handleMouseMove = (event) => { if (isDrawing) { drawRectangle(event); // 检测鼠标是否移出当前矩形范围 checkMouseOutOfRect(event); } }; // 绑定事件到canvas,而非document,减少全局监听 canvas.addEventListener("mousemove", handleMouseMove); return () => { canvas.removeEventListener("mousemove", handleMouseMove); }; }, [isDrawing]); const startDrawingRectangle = ({ nativeEvent }) => { nativeEvent.preventDefault(); nativeEvent.stopPropagation(); // 计算相对canvas的起始坐标 startX.current = nativeEvent.clientX - canvasOffSetX.current; startY.current = nativeEvent.clientY - canvasOffSetY.current; setIsDrawing(true); }; const drawRectangle = ({ nativeEvent }) => { if (!isDrawing || !startX.current || !startY.current) return; const context = contextRef.current; const canvas = canvasRef.current; if (!context || !canvas) return; const newMouseX = nativeEvent.clientX - canvasOffSetX.current; const newMouseY = nativeEvent.clientY - canvasOffSetY.current; // 计算矩形的左上角和右下角坐标 const xtl = Math.min(startX.current, newMouseX); const ytl = Math.min(startY.current, newMouseY); const xbr = Math.max(startX.current, newMouseX); const ybr = Math.max(startY.current, newMouseY); const rectWidth = xbr - xtl; const rectHeight = ybr - ytl; // 清空画布并绘制新矩形 context.clearRect(0, 0, canvas.width, canvas.height); context.strokeRect(xtl, ytl, rectWidth, rectHeight); // 存储当前矩形信息,用于后续移出检测 currentRect.current = { xtl, ytl, xbr, ybr }; }; const checkMouseOutOfRect = ({ nativeEvent }) => { if (!currentRect.current) return; const mouseX = nativeEvent.clientX - canvasOffSetX.current; const mouseY = nativeEvent.clientY - canvasOffSetY.current; const { xtl, ytl, xbr, ybr } = currentRect.current; // 判断鼠标是否在矩形外 if (mouseX < xtl || mouseX > xbr || mouseY < ytl || mouseY > ybr) { stopDrawingRectangle(); } }; const stopDrawingRectangle = () => { setIsDrawing(false); const canvas = canvasRef.current; const context = contextRef.current; if (canvas && context) { context.clearRect(0, 0, canvas.width, canvas.height); } // 重置所有临时变量 startX.current = null; startY.current = null; currentRect.current = null; }; return ( <div style={{ position: 'relative' }}> <video ref={videoRef} style={{ width: '100%', height: 'auto' }} /> <canvas ref={canvasRef} style={{ position: 'absolute', top: 0, left: 0, cursor: 'crosshair' }} onMouseDown={startDrawingRectangle} onMouseUp={stopDrawingRectangle} onMouseLeave={stopDrawingRectangle} /> </div> ); }; export default CanvasDrawer;
关键修复点
- 恢复
setIsDrawing(false):在stopDrawingRectangle中设置状态为false,彻底停止绘制逻辑 - 添加矩形移出检测:通过
checkMouseOutOfRect函数判断鼠标位置,移出则清空画布并停止绘制 - 优化事件绑定:将
mousemove绑定到canvas而非document,减少不必要的全局事件触发 - 初始化偏移量:在useEffect中设置canvas的偏移量,确保坐标计算准确
- 临时变量重置:停止绘制时清空所有临时存储的坐标和矩形信息
内容的提问来源于stack exchange,提问作者hse
相关产品推荐
相关产品推荐

