如何在dragover事件触发时复用handleClick逻辑并识别光标下矩形
解决拖拽时识别光标下Section的问题
问题根源
- 仅将
groupRef传给根Section组件,子Section未接收该ref,无法追踪子节点,且单个ref无法适配多层Section结构。 - 原生
dragover事件并非Konva触发,不能直接调用e.findAncestor(),需手动处理指针坐标并查找对应节点。 useEffect依赖groupRef但ref对象本身不会触发重渲染,导致事件绑定时机与节点更新不同步。
解决方案
1. 抽离通用选择逻辑
把Section点击和拖拽的选择逻辑抽成独立函数,统一处理Konva事件和原生拖拽事件:
// 通用Section选择函数 const selectTargetSection = (event, store, stageRef) => { let targetSectionNode = null; // 处理原生拖拽事件 if (event.type === 'dragover') { event.preventDefault(); if (!stageRef.current) return; // 手动设置舞台指针位置,适配Konva的坐标系统 stageRef.current.setPointersPositions(event); const pointerPos = stageRef.current.getPointerPosition(); if (!pointerPos) return; // 通过坐标查找最上层的可交互节点 const hitNode = stageRef.current.getIntersection(pointerPos); targetSectionNode = hitNode?.findAncestor('.section'); } // 处理Konva点击事件 else { targetSectionNode = event.target.findAncestor('.section'); } // 更新选中状态 if (targetSectionNode) { // 用节点id关联section的id(需要在Section组件中绑定) store.selectedSectionId = targetSectionNode.id(); } else if (event.target.nodeType === "Stage") { store.selectedSectionId = null; } };
2. 修改Section组件,绑定Section ID到节点
给每个Section的Group节点设置唯一ID(对应section的id),方便后续识别:
function SectionInner(props) { // ... 原有代码 return ( <Group x={x} y={y} onClick={(e) => selectTargetSection(e, props.store)} id={props.section.id} // 绑定section的id到Konva节点 name="section" > {/* ... 原有子组件 */} </Group> ); }
3. 调整RootFrame的拖拽事件处理
直接在dragover中调用通用函数,无需依赖单个ref:
function RootFrame(props) { // ... 原有代码 const dragover=(e)=>{ selectTargetSection(e, props.store, stageRef); } useEffect(() => { const con = stageRef.current.container(); con.addEventListener('dragover', dragover); con.addEventListener('drop', function (e) { e.preventDefault(); // ... 原有drop逻辑 }); // 清理事件监听,防止内存泄漏 return () => { con.removeEventListener('dragover', dragover); }; }, []) // stageRef是稳定引用,无需额外依赖 // 修改RootFrame的点击事件,复用通用函数 const handleClick = (e) => { selectTargetSection(e, props.store, stageRef); }; // ... 原有return代码,移除Section的groupRef传递 return ( <div id='1'> {/* ... 原有拖拽元素 */} <Stage width={800} height={height} ref={stageRef} onClick={(e) => handleClick(e)} id="container" > <Provider store={window.store}> <Layer scaleX={scale} scaleY={scale} y={20} x={20}> <Section section={root.sections[0]} x={root.frameSize} y={root.frameSize} {/* 移除不必要的groupRef传递 */} /> <Metrics /> </Layer> </Provider> </Stage> </div> ); }
关键改进点
- 抛弃单个ref追踪节点的方案,改用Konva的
getIntersection通过坐标查找节点,适配多层Section结构。 - 统一处理点击和拖拽事件的选择逻辑,避免重复代码。
- 修复事件监听的清理逻辑,防止内存泄漏。
- 移除不必要的ref传递,简化组件结构。
内容的提问来源于stack exchange,提问作者Петя Герман
相关产品推荐
相关产品推荐

