Canvas画线算是最基础的操作,涉及的方法也比较简单
1 | _cv = _canvas.getContext('2d'); |
点很多的话,加个 for
循环也轻松搞定,如果画的是透明线,只需用 RGBA 格式即可,比如上图颜色为1
_cv.strokeStyle = 'rgba(253, 150, 38, 0.41)'
这个方法在已知画笔路径点的时候,是可以画出透明线,透明叠加起来效果也很好
但是如果是实时作图的,因为 lineTo()
只是画路径,并不会描线,所以需要不断调用 stroke()
来画出可见线条
但这么做的话,如果是透明的线,就会出现问题
只有线最后一点是透明,其它部分全部变成不透明了
因为在 lineTo()
的时候,每加一点,就 stroke()
,这样实际上变成
- 1 => 2 渲染
- 1 => 2 => 3 渲染
- 1 => 2 => 3 => 4 渲染
- 1 => 2 => 3 => 4 => 5 渲染
所以原来透明的线,渲染覆盖了几次后,变成不透明
然后我就想着,那可以分段渲染,每次 stroke()
时都先跳到最后渲染的点,然后画新路径,这样就只渲染新增的部分
- 1 => 2 渲染
- moveTo 2 => 3 渲染
- moveTo 3 => 4 渲染
- moveTo 4 => 5 渲染
然而就是这样
线头跟线头重叠,透明度有变化,导致了很多圆点,线头设置的是圆头 _cv.lineCap = 'round'
如果改成平头 _cv.lineCap = 'butt'
是这样子
无意间发现Canvas有个属性 globalAlpha
,是全局透明度,想到可以用2个canvas叠加
上层是实时作画区,下层则是已画好的线,上层设置全局透明,strokeStyle
则设置不透明,就可以实现实时画透明线
画出的线提笔后(mouseUp),再把上层图像合成到下层去,同时清空上层canvas
1 | _cv1 = can1.getContext('2d') |
然后某天在 知乎 有大佬提供了另一个方法 stackoverflow
大概就是先把当前canvas保存起来,然后每画一个点的时候
- 恢复存档,1 渲染
- 恢复存档,1 => 2 渲染
- 恢复存档,1 => 2 => 3 渲染
- 恢复存档,1 => 2 => 3 => 4 渲染
1 | paths.push([pos]); // Add new path, the first point is current pos. |
我把这个加到项目里,canvas 实际分辨率是 1512*1080,处理起来丝毫不会延迟