Quiet
  • HOME
  • ARCHIVE
  • TAGS
  • ABOUT ME

Jesslyn wongkayan

  • HOME
  • ARCHIVE
  • TAGS
  • ABOUT ME
Quiet主题
  • Canvas
  • Html5

项目中的Canvas brush

Jesslyn

2022-10-24 16:33:36

基本功能

  1. brush 标注出 shape
  2. 选中所选的 shape
  3. 移动当前所选 shape

所遇问题以及解决方法

问题1: 当 brush 在 canvas 上画的时候,总是进行每一帧的不断重绘。重绘的整个过程,底层不仅仅是把当前 brush 的地方给绘制,而且还会把之前通过 brush 过的地方进行重绘。如此一来,当标注的地方越来越多的时候,计算得越来越多,canvas 性能损耗越来越大,此时会出现闪屏的情况。

解决办法:进行多图层操作。

此时把 canvas 分成 cluster 层和 active 层。cluster 层主要放的 brush 完形成的标注。active 层主要放的是当前笔刷画的标注。

当画完当前标注完后,对应画完的图形放到了从 active 层放到了 cluster 层。通过 requestAnimationFrame 把所有标注的图像数据绘制到cluster 层。

问题2: 如何把不同 brush 出来的图形,进行合成。

合成不外乎就是两种方法,一种是 drawImage 另一种是 通过 putImageData。接下来分析两种方法。以下想了两种方法:

结论先行:把所有 active 图层下绘制好的 shape 获取 imageData,把 imageData 用缓存存下来,合成的时候,利用 imageData 形成离屏 canvas,通过离屏 canvas,drawImage

drawImage: 绘制到上下文的元素。允许任何的画布图像源,例如:HTMLImageElement、SVGImageElement (en-US)、HTMLVideoElement、HTMLCanvasElement、ImageBitmap、OffscreenCanvas 或 VideoFrame (en-US)。

putImageData(不可行)原因如下:

  1. Shape 像素覆盖问题:getImageData和putImageDataList,但是出现不了合成情况,两个imageData的每一个pixcel像素只能选择一个新的像素呈现。问题在于也需要合成,也就是说,当 shape 有重合部分的时候,当前像素只能显示其中一个的 shape 对应的像素,无法完全展示两个不同的 shape。
  2. globalComposite 的合成效果行不通。 复合模式 globalComposite 不会影响 putImageData。putImageData 是比较低级别的。因为putImageData 只是直接替换生成的位图上的像素。

较低级别的意思是它绕过上下文和它用来改变用其他方法(翻译、样式等)绘制的东西的参数 https://stackoverflow.com/questions/18927109/does-globalcompositeoperation-source-over-work-with-putimagedata

  1. 对比 putImageData 和 drawImage 的效率,《HTML5 Canvas 核心技术》

问题3: brush出来的在 active层 效果太粗,使得图片看得不清晰,使得标注有偏差。

奇怪的是同样的合成效果是 source-over,active 层画出来的会更粗一些,而cluster 层画出来的透明度是正常的。这个问题至今还不知道什么原因。有朋友知道的可以留言告诉我。

于是 active 层效果用了 xor,让原本颜色变得稍微透明些。cluster 层用 source-over,使得最近画的 shape 盖在上面

问题4 :绘制图案好了之后,如果需要移动某个 shape,如何选中当前点击的 shape 呢?

对于这个问题,可以分解为以下三个子问题:

  1. 点击位置是否在图形里面
  2. 怎么知道点击位置选中哪个 shape?
  3. 遇到点击坐标存在重合图形的时候,选中优先级怎么安排?

碰撞检测算法检测两个图形是否出现碰撞。解决这个问题,可以类比碰撞检测算法。实际上需要计算一个点是否与 shape 出现碰撞。

碰撞检测算法:外接图形(矩形、圆)、光线投射法、分离轴定理(不同角度判断投影是否有重叠)——《html5 canvas核心技术》

brush 形状不是规整的固定,因此对于以上算法,可行性不大。最好是通过像素计算来判断。于是有以下做法解决上面三个子问题

  • 是否选中和选中哪个: 根据imageData索引判断透明度 > 0
  • 优先级:如果有多个选中,那么绘制后的选中(优先级机制,需要与产品进行讨论出来)

优点: 思路最直接、结果最准确、而且对图形形状没有任何要求的方法

缺点:当图形需要在画布上移动时,要频繁的创建数据缓存才能保证检测结果准确,受到画布尺寸和图形数量的影响, getImageData() 方法的性能会成为严重的瓶颈。所以如果canvas图形是静态的,这个方法非常适合,否则就不适合用这个方法了。

当图形的填充颜色完全透明时,这种检测方法就会失效,需要做一些特殊的处理。Create.js 的解决方案是给这个透明图形绑定一个大小一致的不透明的图形,用这个图形来做检测。

上一篇

零基础挑战一天学习VUE

下一篇

前端性能优化

©2023 By Jesslyn wongkayan.
Quiet主题