Skip to main content

使用 JavaScript 和 Konva 实现 Canvas 拖放

Canvas API 不会为绘制的形状提供拖放功能。原生 Canvas 应用必须实现以下四个部分:

  1. 存储每个形状及其位置
  2. 检测指针下方的形状
  3. 将浏览器坐标转换为 Canvas 坐标
  4. 在拖动期间更新形状并重绘场景

指针事件通过一个事件模型支持鼠标、手写笔和触摸输入。在原生 Canvas 实现中,pointerdown 之后调用 setPointerCapture()。捕获会使画布外的移动事件保持活动状态。

拖动 Konva 节点

Konva 实现了命中检测和指针跟踪。在节点上设置 draggable。然后将其最终位置存储在应用状态中。

import { useState } from 'react';
import { Stage, Layer, Rect, Text } from 'react-konva';

const App = () => {
const [position, setPosition] = useState({ x: 80, y: 100 });
const [dragging, setDragging] = useState(false);

return (
<Stage width={window.innerWidth} height={380}>
<Layer>
<Text
x={20}
y={20}
text={`Position: ${Math.round(position.x)}, ${Math.round(position.y)}`}
fontSize={18}
/>
<Rect
x={position.x}
y={position.y}
width={160}
height={100}
fill={dragging ? '#ff922b' : '#4dabf7'}
stroke="#1f2937"
strokeWidth={3}
shadowBlur={dragging ? 12 : 0}
cornerRadius={10}
draggable
onDragStart={() => setDragging(true)}
onDragMove={(event) => {
setPosition({ x: event.target.x(), y: event.target.y() });
}}
onDragEnd={(event) => {
setDragging(false);
setPosition({ x: event.target.x(), y: event.target.y() });
}}
/>
</Layer>
</Stage>
);
};

export default App;

Konva 会在拖动期间更改节点位置。示例还会将该位置写入 React 状态。此状态会使应用模型保持同步。

对于大型场景,不要在每个 dragmove 事件中更新无关的 React 状态。在拖动期间更新 Konva 节点。当其他组件不需要实时坐标时,在 dragend 时保存最终应用状态。

Canvas 形状不是键盘控件。为每个必要的拖动操作提供一个 HTML 控件。该控件可以按固定步长移动同一个形状。

请参阅拖放指南,其中提供了 Vanilla、React、Vue、Svelte 和 Angular 示例。