Skip to main content

HTML5 Canvas 自定义命中检测函数教程

改变图形命中区域有两种方式:hitFunchitStrokeWidth 属性。

1. 什么是 hitFunc

要为 Konva 中的图形创建自定义命中绘制函数,可以设置 hitFunc 属性。命中绘制函数是 Konva 用来绘制用于命中检测的区域的函数。使用自定义命中绘制函数有多种好处,比如使命中区域更大,方便用户与图形交互;只让图形的某些部分可以被检测到,其他部分则不行;或者简化命中绘制函数以提升渲染性能。

另外,可以查看一些编写自定义 sceneFunc最佳实践sceneFunc 也可用于 hitFunc

hitFunc 是一个带有两个参数的函数:Konva.Context 渲染器和一个图形实例。

2. 什么是 hitStrokeWidth

对于某些图形,比如 Konva.Line,覆盖 hitFunc 太困难。在某些情况下,你可能只是想让它的事件响应范围更宽一点。这种情况下,最好使用 hitStrokeWidth 属性设置一个较大的值。

操作说明:在星形上进行 mouseover、mouseout、mousedown 和 mouseup 操作,观察命中区域是一个包裹图形的超大圆。对线条也试试看。同时你可以切换命中画布,观察其显示效果,这对调试很有用。

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

// 自定义命中函数
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 = '切换命中画布';
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);
}

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

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

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