canvas 元素拾取

canvas isPointInPath/isPointInStroke 元素拾取

背景

在 2D 画布应用中,需要判断用户点击/悬停的是哪一个可交互元素。常见解决方案包括:

方案 优点 缺点
isPointInPath 实现成本低, 系统提供默认方法, 性能优越 一般需要配合其他方法处理 (BVH 做初筛), 对于大批量渲染场景处理性能不够友好 , 对于不规则图片难以处理
colorPick 选取快速, 对于渲染大量不规则图形,需要频繁拾取的场景性能优异 需要额外的渲染成本

关于 colorPick 可以参考我的另外一篇文章 colorPick

canvas 元素 拾取的大致流程

拾取初筛

为什么需要做一个初筛?

  • 过滤掉大部分不可能命中的元素
  • 图形可能不规整, 凹多边形/凸多边形, 判断是否命中的逻辑过于复杂, 用简单检测代替复杂检测

BVH 介绍 qTree 四叉树介绍

如何做初筛?

  • 过滤掉不可能命中的元素 -> BVH/qTree 这是两种比较常见的方法过滤不会命中的元素
  • 采用包围盒做一个命中的初筛
    • 2d 场景中 AABB 包围盒(轴对齐包围盒), 在 3d 场景中经常使用球体. 也有一些其他包围盒.
    • AABB包围盒比较容易获取, 并且计算简单, 3d 场景中球体最容易计算 (只需要判断点和目标圆心的距离既可)
    • 包围盒本身和视觉实体存在误差
    • 包围盒只是用来做初步检测的得到 没有命中/可能命中 两个答案, 没有命中 自然直接结束 可能命中 则需要进一步处理
    • 也存在某些非高精度要求场景直接使用包围盒替代命中检测的情况

大致流程图

非高精度场景示例图(3d 场景中常见)

在图片上可以看到,当采用凸多边形包围盒在某些场景中, 可以替代精准的拾取计算, 特别是在 3d 场景中.

对于线段/绘制的拾取

用户的绘制其实是一系列点的集合,如何拾取一条线段可以使用 isPointInPath 来拾取, 但是单点拾取难度高, 并且用户绘制时可能设置了画笔粗细, 另外可能有光滑曲线设置 -> 连续的贝塞尔曲线转换

可以参考的方法

  • 依旧使用 isPointInPath , 而不是 isPointInStroke. 但是将每个点的纵坐标(y) +- 1,做一遍拾取, 也就是拾取三条线段组合成一条线段的拾取, 更加符合用户逻辑
  • 根据 path 和 stroke 的宽度, 手动给线段构建一个 stroke 的实体, 作为一个区域, 通过 isPointInStroke 来拾取.

对于不规则图片的拾取处理

  • 采用 colorPick 方法, 一劳永逸的解决问题, 代价是 offsetScreenCanvas 重新渲染一遍
  • 手动给图片构建 外部的粗略包围盒 path, 再通过 isPointInStroke 判断拾取, 具体算法可以去检索一下
  • 偷个懒,直接用图片的 AABB 包围盒 (也就是图片的实际大小) 代替图片内容做拾取

总结

canvas 元素拾取需要的方法 初筛工具 -> BVH, qTree 等 精准检测工具 isPointInPath, isPointInStroke

对于某些不规则图形来说, 需要手动构建对应的 path , 或者采用 colorPick 方案, 或者采用近似解.

相关推荐
IT_陈寒2 分钟前
Redis持久化丢失数据的坑,这次终于被我填平了
前端·人工智能·后端
独泪了无痕1 小时前
Lodash-JavaScript的实用工具库
前端·javascript
有趣的老凌1 小时前
用 Vibe Coding 搭了一个完整小程序「一定能成」
前端·javascript·后端
kyriewen12 小时前
Anthropic 估值逼近万亿美元,Claude Sonnet 5 + Claude Science 一天两连发
前端·ai编程·claude
小徐_233313 小时前
Wot UI 2.2.0 发布:Button 新增 subtle,VideoPreview 预览体验继续增强
前端·微信小程序·uni-app
天蓝色的鱼鱼15 小时前
关于 CSS 你可能不知道的属性,但关键时刻很有用
前端·css
泯泷16 小时前
第 2 篇:设计第一套字节码:Opcode、Instruction 与 Constant Pool
前端·javascript·安全
妙码生花16 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(十五):优化细节、网络请求封装
前端·后端·ai编程
泯泷16 小时前
第 1 篇:从 1 + 2 开始:亲手写出第一台 JSVM
前端·javascript·安全