在 Konva 中实现免费绘图工具有多种方法。
我看到的两种最常见和简单的方法是:
- 基于 Konva 的矢量图形(简单)
- 手动绘制到 2D 画布(高级)
使用 Konva 节点进行自由绘图
因此,第一个也是可能是最简单的方法是:
- 在
mousedown
/touchstart
时开始新的Konva.Line
- 在
mousemove
/touchmove
时向线条添加新点
这种方法对于许多应用程序都能正常工作。此外,简单地将绘图状态以矢量表现形式存储在某个地方(如 React 存储或 JSON 存储到数据库)也是非常容易的。
显示源代码!
<html>
<head>
<script src="https://unpkg.com/konva@9.3.18/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Free Drawing Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f0f0f0;
}
</style>
</head>
<body>
Tool:
<select id="tool">
<option value="brush">Brush</option>
<option value="eraser">Eraser</option>
</select>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight - 25;
// first we need Konva core things: stage and layer
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
var layer = new Konva.Layer();
stage.add(layer);
var isPaint = false;
var mode = 'brush';
var lastLine;
stage.on('mousedown touchstart', function (e) {
isPaint = true;
var pos = stage.getPointerPosition();
lastLine = new Konva.Line({
stroke: '#df4b26',
strokeWidth: 5,
globalCompositeOperation:
mode === 'brush' ? 'source-over' : 'destination-out',
// round cap for smoother lines
lineCap: 'round',
lineJoin: 'round',
// add point twice, so we have some drawings even on a simple click
points: [pos.x, pos.y, pos.x, pos.y],
});
layer.add(lastLine);
});
stage.on('mouseup touchend', function () {
isPaint = false;
});
// and core function - drawing
stage.on('mousemove touchmove', function (e) {
if (!isPaint) {
return;
}
// prevent scrolling on touch devices
e.evt.preventDefault();
const pos = stage.getPointerPosition();
var newPoints = lastLine.points().concat([pos.x, pos.y]);
lastLine.points(newPoints);
});
var select = document.getElementById('tool');
select.addEventListener('change', function () {
mode = select.value;
});
</script>
</body>
</html>
手动绘图
第一种方法有其限制,如果我们想直接使用某些低级 2D 画布 API。如果需要高级访问画布,最好使用 原生上下文访问
我们将创建一个特殊的离屏画布,在其中添加所有绘图。
通过原生访问画布,我们可以使用低级的 2D 上下文函数。
为了在舞台上显示画布,我们将使用 Konva.Image
。
显示源代码!
<html>
<head>
<script src="https://unpkg.com/konva@9.3.18/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Free Drawing Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f0f0f0;
}
</style>
</head>
<body>
Tool:
<select id="tool">
<option value="brush">Brush</option>
<option value="eraser">Eraser</option>
</select>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight - 25;
// first we need Konva core things: stage and layer
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
var layer = new Konva.Layer();
stage.add(layer);
// then we are going to draw into special canvas element
var canvas = document.createElement('canvas');
canvas.width = stage.width();
canvas.height = stage.height();
// created canvas we can add to layer as "Konva.Image" element
var image = new Konva.Image({
image: canvas,
x: 0,
y: 0,
});
layer.add(image);
// Good. Now we need to get access to context element
var context = canvas.getContext('2d');
context.strokeStyle = '#df4b26';
context.lineJoin = 'round';
context.lineWidth = 5;
var isPaint = false;
var lastPointerPosition;
var mode = 'brush';
// now we need to bind some events
// we need to start drawing on mousedown
// and stop drawing on mouseup
image.on('mousedown touchstart', function () {
isPaint = true;
lastPointerPosition = stage.getPointerPosition();
});
// will it be better to listen move/end events on the window?
stage.on('mouseup touchend', function () {
isPaint = false;
});
// and core function - drawing
stage.on('mousemove touchmove', function () {
if (!isPaint) {
return;
}
if (mode === 'brush') {
context.globalCompositeOperation = 'source-over';
}
if (mode === 'eraser') {
context.globalCompositeOperation = 'destination-out';
}
context.beginPath();
var localPos = {
x: lastPointerPosition.x - image.x(),
y: lastPointerPosition.y - image.y(),
};
context.moveTo(localPos.x, localPos.y);
var pos = stage.getPointerPosition();
localPos = {
x: pos.x - image.x(),
y: pos.y - image.y(),
};
context.lineTo(localPos.x, localPos.y);
context.closePath();
context.stroke();
lastPointerPosition = pos;
// redraw manually
layer.batchDraw();
});
var select = document.getElementById('tool');
select.addEventListener('change', function () {
mode = select.value;
});
</script>
</body>
</html>