HTML5 Canvas Optimizing Strokes Performance Tip

Disable shadow for stroke

By default Konva is making extra internal drawing when a shape has both stroke and shadow. It is doing that to make drawing result look expected.

If you don’t really need shadow for stroke you should set shape.shadowForStrokeEnabled(false). Remember that shadow will be disable if you are using Konva.Line without fill (because it is just stroked shape). Disabling shadow for stroke will increase rendering speed A LOT.

Remove stroke from hit

If you have a shape with fill and very small stroke you can set shape.hitStrokeWidth(0) to remove stroke from hit graph.
Don’t use this property if your stroke is critical for hit detection (like non closed lines). By default, Konva is using strokes for hit graph - for detecting shapes under pointer for events. In some situations you may not need that.

Instructions: take a look into very slow random movements of circles. Now try to toggle optimized rendering checkbox. Faster?

Konva Optimizing Strokes Demoview raw
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/[email protected]/konva.min.js"></script>
<meta charset="utf-8" />
<title>Konva Optimizing Strokes Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f0f0f0;
}
label {
position: absolute;
top: 50px;
left: 0;
background-color: white;
padding: 3px;
border-radius: 3px;
}
</style>
<script src="https://mrdoob.github.io/stats.js/build/stats.min.js"></script>
</head>
<body>
<div id="container"></div>
<label>
<input type="checkbox" id="checkbox" />
Use optimized strokes
</label>
<script>
var width = window.innerWidth;
var height = window.innerHeight;

var stage = new Konva.Stage({
container: 'container',
width: width,
height: height,
});
var layer = new Konva.Layer();
stage.add(layer);

var n = 100,
shape;
for (var i = 0; i < n; i++) {
shape = new Konva.Circle({
x: stage.width() * Math.random(),
y: stage.height() * Math.random(),
radius: 10 + 10 * Math.random(),
fill: Konva.Util.getRandomColor(),
stroke: 'black',
shadowColor: 'black',
draggable: true,
shadowOffset: {
x: 5,
y: 5,
},
});
layer.add(shape);
}

var stats = new Stats();
document.body.appendChild(stats.domElement);
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';

stats.begin();
const anim = new Konva.Animation(() => {
stats.end();
layer.children.forEach((child) => {
child.move({
x: (Math.random() - 0.5) * 5,
y: (Math.random() - 0.5) * 5,
});
});
stats.begin();
}, layer);
anim.start();

document.querySelector('#checkbox').addEventListener('change', (e) => {
const shouldOptimize = e.target.checked;
layer.children.forEach((child) => {
child.hitStrokeWidth(shouldOptimize ? 0 : 'auto');
child.shadowForStrokeEnabled(shouldOptimize ? false : true);
});
});
</script>
</body>
</html>