免费绘图 Konva 演示

在 Konva 中实现免费绘图工具有多种方法。

我看到的两种最常见和简单的方法是:

  1. 基于 Konva 的矢量图形(简单)
  2. 手动绘制到 2D 画布(高级)

使用 Konva 节点进行自由绘图

因此,第一个也是可能是最简单的方法是:

  1. mousedown/touchstart 时开始新的 Konva.Line
  2. mousemove/touchmove 时向线条添加新点

这种方法对于许多应用程序都能正常工作。此外,简单地将绘图状态以矢量表现形式存储在某个地方(如 React 存储或 JSON 存储到数据库)也是非常容易的。

显示源代码!

Canvas Scrolling Largeview raw
<!DOCTYPE html>
<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

显示源代码!

Canvas Scrolling Largeview raw
<!DOCTYPE html>
<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>