• home > webfront > visualization > comprehensive >

    canvas图形拾取方案汇总:isPointInPath/getImageData与几何运算

    Author:zhoulujun Date:

    由于 Canvas 不会保存绘制图形的信息,一旦绘制完成用户在浏览器中得到的是一张图片(图形库会保存结构,比如Konva Tree),如何实现实现canvas拖拽?内置API:isPointInPath isPointInStrokey、getImageData,几何运算如何做?

    使用canvas绘制图形的时候,也是经常会有交互的。 在canvas中是没有js 操作 dom的那么方便的事件系统。

    从功能上来说 SVG 的功能更全面一些,提供的接口更丰富,使用更简单,但是所有的渲染和拾取(点击获取图形)都是浏览器内置的行为,其性能跟浏览器相关;而 Canvas 是使用一种直接绘制的方式(2D图形库也会对图形进行管理,比如Konva Tree),其渲染性能和拾取性能跟用户的实现的方式密切相关,性能的优化空间非常大。

    本文关注 Canvas 的拾取的方案以及各种方案的优缺点,提供给用户在合适的场景下选择恰当的拾取方式。

    canvas图形拾取方案

    由于 Canvas 不会保存绘制图形的信息,一旦绘制完成用户在浏览器中得到的是一张图片,用户在图片上点击时时不能获取对应的图形信息,所以需要缓存图形的信息,根据用户点击的位置进行判断击中了那些图形。常见的拾取方案有以下几种:

    1. 使用 Canvas 内置的 API 拾取图形

    2. 使用几何运算拾取图形

    3. 使用缓存离屏渲染 Canvas 通过颜色拾取图形

    4. 混杂上面的几种方式来拾取图形

    内置原生API

    isPointInPath 是 canvas 原生提供的一个检测某个点是否在指定路径内的方法。

    isPointInPath 如果指定的点位于当前路径中,则返回 true,否则返回 false。 ⚠不支持strokeRect(isPointInStroke)、fillRect。

    具体参看:https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/isPointInPath

    缺点:

    1. 判断光标点中哪个元素,需要遍历元素,去调用方法,直到返回 true 为止,性能可能会差一点(可以用四叉树碰撞检测,减少需要遍历的元素数量,但极端情况可能还是会有很多元素,另外可通过包围盒减少计算量);

    2. 检测点是否在一条 strokeWidth 较大的线上可能会有错误,因为路径是没有宽度的;

    通过颜色拾取图形

    取色法是一种高效的图形拾取技术,其核心思想是:通过Canvas渲染上下文对象提供的ctx.getImageData(x, y, 1, 1)方法,可以精确地获取指定位置像素的颜色信息

    根据真正的 canvas 元素,额外创建一个大小相同离屏的缓存 canvas 元素。

    每次我们在主 canvas 上绘制形状时,也在缓存 canvas 上绘制同样形状的纯色块,并用哈希表记录颜色和对应的图形对象,比如红色表示矩形 A,绿色表示矩形 B。

    Canvas取色法的实现原理Canvas取色法的实现图形选取

    然后当我们在真实 canvas 上点击时,我们在 canvas 绑定事件,就可以拿到坐标位置 (x, y),再通过 offScreenCtx.getImageData(x, y, 1, 1) 方法得到缓存 canvas 的对应像素点的颜色值,然后找到它对应的图形对象,执行其注册的事件。

    由于缓存图形中的颜色都是唯一的,因此可以迅速从颜色对照表中找到对应的图形对象,实现精准拾取。需要注意的是,ctx.getImageData()方法返回的数据中包含了data属性,该属性为Uint8ClampedArray类型,存储了像素数据。每个像素由四个字节的值构成,分别代表红、绿、蓝和透明度(r,g,b,a)。

    对于常见的几何图形,取色法可以直接在缓存图形上进行绘制。然而,对于文本和图像这类较为复杂的图形对象,则需要将其转换为矩形,并以矩形的方式绘制在缓存图形中。这样做可以确保取色法的有效性和准确性,使其能够广泛应用于各种场景。

    https://konvajs.org/ 库就是基于这个实现。

    从上述原理可以看出来,Konva 对于不规则图形的匹配依然很精确,但缺点也很明显,每次都需要绘制两份,导致绘制性能变差

    同时,getImageData 耗时比较高,在频繁触发的场景(onWheel)会导致帧率下降严重



    图形学算法:几何坐标计算

    利用canvas的event事件中的x、y来判断, 基础的图形(正方形、圆形,直线)自然好判断,如果是多边形、不规则图形就有一些计算的成本。

    某种意义上是 isPointInPath 的底层实现,能做到平台无关。

    具体办法参看 判断点在多边形|圆形内的方实现方式分析及代码实现

    缺点:

    1. 和 isPointInPath 方案一样,需要遍历图形检测;

    2. 实现复杂,简单图形还算简单,但如果涉及到贝塞尔曲线等复杂形状,实现就会很复杂且性能堪忧(可以考虑用 isPointInPath);

    3. 如果使用了 transform,因为要进行矩阵乘法,性能会有所下降。

    几何法是 AntV 和飞书文档采用的实现方式,实现方式相对复杂一些,针对不规则图形的匹配效率偏低。

    几何法的优势在于不需要在内存里面进行重复绘制,但依赖于复杂的几何计算,因此不适合有大量不规则图形的情况

    在 AntV 里面支持对不规则图形的匹配,但飞书文档由于是表格业务,所以可以将所有图形都当做矩形来处理,反而更简单一些。



                            

    原文链接:

    参考文章:

    Canvas取色法的实现 https://www.graphanywhere.com/faq/canvas-qa/100063/

    如何在 Canvas 上实现图形拾取? https://blog.csdn.net/fe_watermelon/article/details/128249481

    浅谈 Canvas 渲染引擎设计 https://github.com/yinguangyao/blog/issues/84



    转载本站文章《canvas图形拾取方案汇总:isPointInPath/getImageData与几何运算》,
    请注明出处:https://www.zhoulujun.cn/html/webfront/visualization/comprehensive/9235.html