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 方案, 或者采用近似解.

相关推荐
共创splendid--与您携手1 天前
AI读取前端项目生成skill.md
前端·人工智能·ai
San813_LDD1 天前
[C语言]《Dev-C++ 报错解决手册(Day0607 精华版)》
java·前端·javascript
xiaofeichaichai1 天前
Webpack
前端·webpack·node.js
问心无愧05131 天前
ctf show web入门111
android·前端·笔记
唐某人丶1 天前
模型越来越强,我们还需要 Agent 工程吗?—— 从价值重估到 Harness 实践
前端·agent·ai编程
智码看视界1 天前
现代Web开发基础:全栈工程师的起航点
前端·后端·c5全栈
JS菌1 天前
手写一个 AI Agent 全栈项目:从沙箱执行到子智能体的完整实现
前端·人工智能·后端
excel1 天前
HLS TS 文件损坏的元凶:Git 提交与拉取
前端
Aphasia3111 天前
https连接传输流程
前端·面试