Skip to main content

HTML5 canvas Custom Shape Tutorial

To create a custom shape with Konva, you can use the Konva.Shape() object and define a custom drawing function.

When creating a custom shape, you need to define a drawing function that is passed a Konva.Context renderer and a shape instance. Here's a simple rectangle example:

const rect = new Konva.Shape({
x: 10,
y: 20,
fill: '#00D2FF',
width: 100,
height: 50,
sceneFunc: function (context, shape) {
context.beginPath();
// don't need to set position of rect, Konva will handle it
context.rect(0, 0, shape.getAttr('width'), shape.getAttr('height'));
// (!) Konva specific method, it is very important
// it will apply all required styles
context.fillStrokeShape(shape);
}
});

Konva.Context is a wrapper around native 2d canvas context that has the same properties and methods with some additional API.

There are two properties that can be used for drawing custom shapes:

  • sceneFunc - defines visual appearance of a shape
  • hitFunc - optional function to define custom hit region for events (see Custom Hit Region demo)

Best practices for writing sceneFunc and hitFunc:

  1. Optimize the function as it can be called many times per second. Avoid creating images or large objects.
  2. The function should not have side effects like moving shapes, attaching events or changing app state.
  3. Define custom hitFunc when applying complex styles or drawing images.
  4. Don't manually apply position and scaling in sceneFunc. Let Konva handle it through shape properties.
  5. Avoid manual styling in sceneFunc. Use context.fillStrokeShape(shape) for styling.
  6. Reference Konva core shapes implementations for more examples.

For full list of properties and methods, see the Shape API Reference.

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);

const triangle = new Konva.Shape({
  sceneFunc: function (context, shape) {
    context.beginPath();
    context.moveTo(20, 50);
    context.lineTo(220, 80);
    context.lineTo(100, 150);
    context.closePath();
    context.fillStrokeShape(shape);
  },
  fill: '#00D2FF',
  stroke: 'black',
  strokeWidth: 4
});

layer.add(triangle);