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

如何在dragover事件触发时复用handleClick逻辑并识别光标下矩形

解决拖拽时识别光标下Section的问题

问题根源

  1. 仅将groupRef传给根Section组件,子Section未接收该ref,无法追踪子节点,且单个ref无法适配多层Section结构。
  2. 原生dragover事件并非Konva触发,不能直接调用e.findAncestor(),需手动处理指针坐标并查找对应节点。
  3. 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,提问作者Петя Герман

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 01:45:57