如何使用 React 在 Canvas 上实现自由绘制?
本示例展示如何以“React 方式”实现具有完整矢量表示的自由绘制应用。
这种实现适用于许多白板应用。你可以添加 撤销/重做功能,并将完整 state 保存到后端。
注意:如果 state 中包含太多线条,应用速度 会变慢。如果要支持绘制成百上千条线,需要进行额外优化。
本示例展示以下操作:
- 使用
React.useRef跟踪绘制 state,以提高性能 - 将线条以矢量数据形式存储在 React state 中
- 处理绘制所需的鼠标和触摸事件
- 使用
globalCompositeOperation实现画笔和橡皮擦工具 - 使用圆角端点和张力创建平滑线条
import React from 'react';
import { Stage, Layer, Line, Text } from 'react-konva';
const App = () => {
const [tool, setTool] = React.useState('pen');
const [lines, setLines] = React.useState([]);
const isDrawing = React.useRef(false);
const handleMouseDown = (e) => {
isDrawing.current = true;
const pos = e.target.getStage().getPointerPosition();
setLines([...lines, { tool, points: [pos.x, pos.y] }]);
};
const handleMouseMove = (e) => {
// no drawing - skipping
if (!isDrawing.current) {
return;
}
const stage = e.target.getStage();
const point = stage.getPointerPosition();
let lastLine = lines[lines.length - 1];
// add point
lastLine.points = lastLine.points.concat([point.x, point.y]);
// replace last
lines.splice(lines.length - 1, 1, lastLine);
setLines(lines.concat());
};
const handleMouseUp = () => {
isDrawing.current = false;
};
return (
<div>
<select
value={tool}
onChange={(e) => {
setTool(e.target.value);
}}
>
<option value="pen">Pen</option>
<option value="eraser">Eraser</option>
</select>
<Stage
width={window.innerWidth}
height={window.innerHeight}
onMouseDown={handleMouseDown}
onMousemove={handleMouseMove}
onMouseup={handleMouseUp}
onTouchStart={handleMouseDown}
onTouchMove={handleMouseMove}
onTouchEnd={handleMouseUp}
>
<Layer>
<Text text="Just start drawing" x={5} y={30} />
{lines.map((line, i) => (
<Line
key={i}
points={line.points}
stroke="#df4b26"
strokeWidth={5}
tension={0.5}
lineCap="round"
lineJoin="round"
globalCompositeOperation={
line.tool === 'eraser' ? 'destination-out' : 'source-over'
}
/>
))}
</Layer>
</Stage>
</div>
);
};
export default App;