图形编辑器——矩形选区是如何实现选中多个图形的?

大家好,我是曾经的图形编辑器开发者前端西瓜哥,今天继续说说图形编辑器开发需要用到的知识点。

图形编辑器的一个非常基础需求是:可以通过选择工具勾画出一个矩形选区,来选中该区域内的所有图形,以方便之后的批量移动、变形、删除等操作。

那么如何判断这些元素是否在矩形选区,该选择哪种算法,就需要我们好好考虑了。

矩形包含

选区和图形元素属于包含关系,选区必须完全将整个图形包裹住。

需要注意的是,并不是所有的图形都是矩形,这里指的图形元素矩形指的是包围图形的最小矩形。

1652625681-image-20220212143509840.png

只要元素矩形(rect2)的所有点都在选区矩形(rect1)的范围内,就满足包含关系。

思路就是 rect1 的左上角点需要在 rect2 的左上角点的左上方,rect1 的右下角点需要在 rect2 的右下角点的右下方。

rect1.x <= rect2.x &&
rect2.y <= rect2.y &&
rect1.x + rect1.w <= rect2.x + rect2.w &&
rect1.y + rect1.h <= rect2.y + rect2.h

x + w 表示矩形左上角的 x 坐标加上宽度后,得到的右下角位置的 x 坐标。y + h 同理。

1652625779-rect-contain.gif

如图为 Boxy SVG 编辑器效果,选区必须完全选中三角形,才能让三角形进入被选中状态。

矩形重叠

即两个矩阵发生了重叠,属于矩形包含情况的超集。

矩形重叠其实也就是矩形碰撞,是一种常见的 2D 碰撞检测算法。

rect1.x < rect2.x + rect2.width &&
rect1.x + rect1.width > rect2.x &&
rect1.y < rect2.y + rect2.height &&
rect1.height + rect1.y > rect2.y

实现上用到了分类讨论。

首先针对 x 进行降维,变成两个横向线段,判断它们是否有重叠部分。

然后对 y 进行降为纵向线段,判断它们是否有重叠部分。

都为 true,就说明矩形重叠。

1652625820-rect-collision.gif

如图为 Figma 编辑器效果,选区只要和包围三角形的最小矩形有重叠,就能选中图形,即使没有真正框中三角形。

精准判断是否有交点

还有就是这种最精准的碰撞检测,选区矩形必须和图形真正有重叠,才会选中图形。

实现上首先要用到前面的 “矩形重叠” 算法,先通过低成本的计算初步判断二者是否在矩形上重叠。如果不重叠,就说明不会被选中,直接结束。

如果矩形重叠,才做下一步的计算。

这时候你需要考虑矩形和圆形、多边形、椭圆以及最复杂的贝塞尔曲线等各种图形的碰撞算法,以及它们旋转、缩放后导致的复杂情况。这并不是一件简单的事情,只会让人头皮发麻。

此外还有效率的问题,如果选区区域内有非常多的复杂图形,如果你的算法不够高效,那可能会造成卡顿,带来不好的用户体验。

1652625852-perfect-collision-check.gif

如图为 Adobe Illustrator 编辑器的效果,选区必须与图形真正发生了重叠,才能选中图形。

三种方式的对比

第一种方式,是要让选区完全覆盖图形,从行为上比较合理。缺点是如果如果图形一部分在窗口外,就不好选中,要做先缩小画布才能去选中图形。

第二种方式,则是选区和图形的最小包围矩形重叠即可,可以通过框选图形来实现选中。但有时候我们框选了一个空白区域也能选中图形,会让用户感到一丝困惑。

用户体验最好的当然是第三种方式,真正意义上的选区与图形重叠,但技术实现上有很多难点,只有相当成熟的老牌编辑器才会使用这种方式。

总的来说,貌似较多的图形编辑器会选择第一种方式。

结尾

本文讲解了选择工具矩形选区选中多个图形的三种方式,希望对你所有帮助。