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

基于react-konva实现鼠标点击直线式自由区域选择的求助

React-Konva 鼠标点击绘制多边形自由选区实现方案

核心实现逻辑

  • 维护两个核心状态:points 数组存储所有已确认的选区顶点坐标,isDrawing 标记当前是否处于绘制状态
  • 绑定 Konva Stage 的三个关键事件:
    • 左键单次点击:获取当前鼠标在画布上的坐标,推入 points 数组
    • 鼠标移动:如果已经存在至少1个已确认顶点,实时更新临时预览线段的终点坐标,实现绘制轨迹预览
    • 双击画布:当已确认顶点不少于3个时,将第一个顶点坐标追加到 points 数组末尾,闭合选区,结束当前绘制状态
  • 渲染层拆分两个线段实例:一个渲染已确认的折线/闭合多边形,一个渲染实时预览的虚线临时线段,闭合后给多边形填充半透明色标记选中区域

核心代码示例

import { useState, useRef } from 'react';
import { Stage, Layer, Line } from 'react-konva';

const FreeSelectionCanvas = () => {
  // 存储已确认的顶点坐标,格式为[x1,y1,x2,y2,...]
  const [confirmedPoints, setConfirmedPoints] = useState([]);
  // 存储实时预览的临时线段坐标
  const [tempLinePoints, setTempLinePoints] = useState([]);
  // 绘制状态标记,用ref避免事件回调里拿到的是旧状态
  const isDrawingRef = useRef(true);

  // 点击添加顶点
  const handleCanvasClick = (e) => {
    if (!isDrawingRef.current) return;
    const stage = e.target.getStage();
    const pointerPos = stage.getPointerPosition();
    setConfirmedPoints(prev => [...prev, pointerPos.x, pointerPos.y]);
  };

  // 鼠标移动更新临时预览线
  const handleCanvasMouseMove = (e) => {
    if (!isDrawingRef.current || confirmedPoints.length < 1) return;
    const stage = e.target.getStage();
    const pointerPos = stage.getPointerPosition();
    // 临时线起点是最后一个已确认的顶点,终点是当前鼠标位置
    setTempLinePoints([
      confirmedPoints[confirmedPoints.length - 2],
      confirmedPoints[confirmedPoints.length - 1],
      pointerPos.x,
      pointerPos.y
    ]);
  };

  // 双击闭合选区
  const handleCanvasDblClick = () => {
    // 至少3个顶点才能构成闭合区域
    if (confirmedPoints.length < 6) return;
    // 把第一个顶点坐标追加到末尾,实现闭合
    setConfirmedPoints(prev => [...prev, prev[0], prev[1]]);
    isDrawingRef.current = false;
    setTempLinePoints([]);
  };

  return (
    <Stage
      width={window.innerWidth}
      height={window.innerHeight}
      onClick={handleCanvasClick}
      onMouseMove={handleCanvasMouseMove}
      onDblClick={handleCanvasDblClick}
    >
      <Layer>
        {/* 已确认的选区线段/闭合多边形 */}
        <Line
          points={confirmedPoints}
          stroke="#1677ff"
          strokeWidth={2}
          fill={isDrawingRef.current ? 'transparent' : 'rgba(22, 119, 255, 0.2)'}
          closed={!isDrawingRef.current}
        />
        {/* 实时预览的虚线临时线段 */}
        <Line
          points={tempLinePoints}
          stroke="#1677ff"
          strokeWidth={2}
          dash={[4, 4]}
        />
      </Layer>
    </Stage>
  );
};

export default FreeSelectionCanvas;

可扩展优化点

  • 增加右键撤销功能:监听画布 contextmenu 事件,阻止默认右键菜单后,删除confirmedPoints数组末尾的两个坐标值,即可撤销上一步点击的顶点
  • 支持选区二次编辑:选区闭合后给Line组件添加draggable属性,配合Konva的Transformer组件可实现选区的拖拽、缩放、变形操作
  • 边界拦截:点击时判断坐标是否超出Stage可视范围,超出则不加入顶点数组,避免渲染异常
  • 性能优化:如果顶点数量过多,可将Line的perfectDrawEnabled属性设为false,减少重绘计算消耗

内容的提问来源于stack exchange,提问作者Sachin Dongare

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 10:57:01