Canvas 用随机颜色检测点是否在图形内

422次阅读  |  发布于2年以前

本文是 Canvas 检测某点是否在图形内的第三篇,前两篇分别介绍了使用[几何方法实现检测的 Fabric.js] 、使用[透明度实现检测的 Create.js] 。本文主要介绍使用随机颜色实现检测的 Konva.js。Konva.js 和 Create.js 都是用颜色值的方式来检测点是否在图形内部,虽然大体上原理是一致的,但是实现的细节上有一定的区别。

Konva.js 会为画布中每个图形元素产生一个随机的颜色值 colorKey。在渲染过程中,除了生成一个为用户展示的 sceneCanvas 之外,还会在内存中产生一个用于检测的 hitCanvas。所有的图形都会在这个 hitCanvas 重新绘制一遍,并且各个图形的大小和位置属性也保持一致,但是图形内填充的颜色却是对应的 colorKey 值。在用户点击发生点击行为之后,通过获取 hitCanvas 上点击位置的颜色值(即 colorKey),就可以找到 sceneCanvas 中哪个图形元素被点击了。

如下图所示,左侧的是正常渲染的 sceneCanvas,右侧的是 hitCanvas。hitCanvas 里面的每个图形所对应的颜色都是唯一的,通过颜色与图形的对应关系,可以很容易定位出哪个元素被点击了。随机的 hex 颜色最多会会有 256 * 256 * 256 = 16777216 种,也就是说在 Konva.js 最多支持 16777216 个图形做点击检测,这个数量也足够我们开发使用了。

Konva.js 生成随机颜色代码:

constructor(config) {
  super(config);
  // set colorKey
  let key;
  while (true) {
    // 生成随机颜色
    key = Util.getRandomColor();
    if (key && !(key in shapes)) {
      break;
    }
  }
  this.colorKey = key;

  // window.Konva.shapes
  shapes[key] = this;
}

Konva.js 判断点是否在图形内部代码:

_getIntersection(pos) {
  const ratio = this.hitCanvas.pixelRatio;
  const p = this.hitCanvas.context.getImageData(Math.round(pos.x * ratio), Math.round(pos.y * ratio), 1, 1).data;
  const p3 = p[3];
  // fully opaque pixel
  if (p3 === 255) {
    const colorKey = Util._rgbToHex(p[0], p[1], p[2]);
    const shape = shapes[HASH + colorKey];

    // 找到对应的图形
    if (shape) {
      return {
        shape: shape,
      };
    }
    return {
      antialiased: true,
    };
  }
  else if (p3 > 0) {
    // antialiased pixel
    return {
      antialiased: true,
    };
  }
  // empty pixel
  return {};
}

当点击多个图形的重叠区域时,这种方法只能最上层被点击的图形,其他的图形会被忽略。在这种情况下, Konva.js 提供了 getAllIntersections(pos) 方法可获取到所有被点击的图形,这个方法的实现原理与 Create.js 的实现方案是一样的:在内存中将每个图形渲染,判断对应点的透明度是否大于 0 。每个图形做一次渲染和判断,会对性能造成一定的影响,下面是该方法在官方文档中的说明:

getAllIntersections(pos): get all shapes that intersect a point. Note: because this method must clear a temporary canvas and redraw every shape inside the container, it should only be used for special situations because it performs very poorly. Please use the Konva.Stage#getIntersection method if at all possible because it performs much better.


Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8