Skip to main content

HTML5 Canvas Custom Hit Detection Function Tutorial

There are two ways to change hit region of the shape: hitFunc and hitStrokeWidth properties.

1. What is hitFunc?

To create a custom hit draw function for a shape with Konva, we can set the hitFunc property. A hit draw function is the function that Konva will use to draw a region used for hit detection. Using a custom draw hit function can have several benefits, such as making the hit region larger so that it's easier for users to interact with a shape, making some portions of a shape detectable and others not, or simplifying the hit draw function in order to improve rendering performance.

Also take a look into some best practices of writing custom sceneFunc that can be used for hitFunc too.

hitFunc is a function with two arguments: Konva.Context renderer and a shape instance.

2. What is hitStrokeWidth?

For some shapes, like Konva.Line it is too hard to overwrite hitFunc. In some cases you just want to make it thicker for events. In this case it is better to use hitStrokeWidth property with a large value.

Instructions: Mouseover, mouseout, mousedown, and mouseup over the star and observe that the hit region is an over sized circle encompassing the shape. Also try the same for a line. Also you can toggle hit canvas to see how it looks. It may be useful for debugging.

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 text = new Konva.Text({
  x: 10,
  y: 10,
  text: '',
  fontSize: 24,
});
layer.add(text);

const star = new Konva.Star({
  x: stage.width() / 4,
  y: stage.height() / 2,
  numPoints: 5,
  innerRadius: 40,
  outerRadius: 70,
  fill: 'red',
  stroke: 'black',
  strokeWidth: 4,
});

// custom hit function
star.hitFunc(function (context) {
  context.beginPath();
  context.arc(0, 0, 70, 0, Math.PI * 2, true);
  context.closePath();
  context.fillStrokeShape(this);
});

const line = new Konva.Line({
  x: stage.width() * 0.6,
  y: stage.height() / 2,
  points: [-50, -50, 50, 50],
  stroke: 'black',
  strokeWidth: 2,
  hitStrokeWidth: 20,
});

const button = document.createElement('button');
button.innerHTML = 'Toggle hit canvas';
document.body.appendChild(button);
let showHit = false;

button.addEventListener('click', () => {
  showHit = !showHit;
  if (showHit) {
    stage.container().style.border = '2px solid black';
    stage.container().style.height = stage.height() + 'px';
    stage.container().appendChild(layer.hitCanvas._canvas);
    layer.hitCanvas._canvas.style.position = 'absolute';
    layer.hitCanvas._canvas.style.top = 0;
    layer.hitCanvas._canvas.style.left = 0;
  } else {
    layer.hitCanvas._canvas.remove();
  }
});

function writeMessage(message) {
  text.text(message);
  layer.draw();
}

star.on('mouseover mouseout mousedown mouseup', function (evt) {
  writeMessage(evt.type + ' star');
});

line.on('mouseover mouseout mousedown mouseup', function (evt) {
  writeMessage(evt.type + ' line');
});

layer.add(star);
layer.add(line);