Free Drawing Konva Demo

There are many ways to implement free drawing tools in Konva.

The simplest way (object oriented) is to:

  1. Start a new Konva.Line on mousedown/touchstart
  2. Add new point into the line while mousemove/touchmove

But that way has limitation if we want to use some low-level 2d canvas API.

So we will use another approach.
We will create special offscreen canvas where we will add all drawings.
With native access to the canvas we can use low-level 2d context functions.
To display the canvas on the stage we will use Konva.Image.

Instructions: Try on draw inside stroked area.

Konva Elastic Stars Demoview raw
<!DOCTYPE html>
<html>

<head>
<script src="https://cdn.rawgit.com/konvajs/konva/2.4.0/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() / 2;
canvas.height = stage.height() / 2;

// created canvas we can add to layer as "Konva.Image" element
var image = new Konva.Image({
image: canvas,
x: stage.width() / 4,
y: stage.height() / 4,
stroke: 'green',
shadowBlur: 5
});
layer.add(image);
stage.draw();

// 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.addEventListener('mouseup touchend', function () {
isPaint = false;
});

// and core function - drawing
stage.addEventListener('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;
layer.batchDraw();
});



var select = document.getElementById('tool');
select.addEventListener('change', function () {
mode = select.value;
});

</script>

</body>

</html>
Next