Skip to main content

HTML5 Canvas Optimize Strokes Performance Tip

When drawing shapes with strokes and shadows in Konva, there's an extra internal drawing step that occurs. This is because Konva needs to ensure that the stroke's shadow is drawn correctly. However, this can impact performance, especially when dealing with many shapes.

To optimize performance, you can disable the stroke shadow by setting shadowForStrokeEnabled(false). This is particularly useful when you don't need the stroke to cast a shadow.

Below is a demo showing the performance difference with and without stroke shadows:

import Konva from 'konva';

const stage = new Konva.Stage({
  container: 'container',
  width: window.innerWidth,
  height: window.innerHeight,
});

const layer = new Konva.Layer();
stage.add(layer);

// Create shape with shadow for stroke (default)
const circleWithShadow = new Konva.Circle({
  x: 100,
  y: 100,
  radius: 50,
  fill: 'red',
  stroke: 'black',
  strokeWidth: 4,
  shadowColor: 'black',
  shadowBlur: 10,
  shadowOffset: { x: 5, y: 5 },
  shadowOpacity: 0.5,
});

// Create shape without shadow for stroke (optimized)
const circleOptimized = new Konva.Circle({
  x: 250,
  y: 100,
  radius: 50,
  fill: 'red',
  stroke: 'black',
  strokeWidth: 4,
  shadowColor: 'black',
  shadowBlur: 10,
  shadowOffset: { x: 5, y: 5 },
  shadowOpacity: 0.5,
  shadowForStrokeEnabled: false,
});

// Add labels
const defaultLabel = new Konva.Text({
  x: 50,
  y: 170,
  text: 'With Stroke Shadow',
  fontSize: 16,
});

const optimizedLabel = new Konva.Text({
  x: 200,
  y: 170,
  text: 'Without Stroke Shadow\n(Better Performance)',
  fontSize: 16,
});

// Add FPS counter
const fpsText = new Konva.Text({
  x: 10,
  y: 10,
  text: 'FPS: 0',
  fontSize: 16,
});

layer.add(circleWithShadow);
layer.add(circleOptimized);
layer.add(defaultLabel);
layer.add(optimizedLabel);
layer.add(fpsText);

// Create animation to demonstrate performance
const anim = new Konva.Animation((frame) => {
  circleWithShadow.rotation(frame.time * 0.1);
  circleOptimized.rotation(frame.time * 0.1);
  
  // Update FPS counter
  fpsText.text('FPS: ' + frame.frameRate.toFixed(1));
}, layer);

anim.start();