使用canvas&svg绘制线条
使用过svg或者canvas来绘制线条的朋友应该比较清楚,在默认情况下,绘制出的线条粗细是均匀的,如下图所示:
使用均匀的线条能实现的效果有限,例如,无法实现艺术字这样的效果。
那么,我们如何绘制出这种带有“压感”的线条呢?
对于支持压感的硬件设备来说,通过PointerEvent事件我们可以轻松获取到指针滑动时的压力(pressure),然后我们就可以根据这个压力来改变绘制线条的粗细,以实现带压感的线条。但是对于鼠标这样不支持压力的设备来说,这个方法就没有效果了,只能另辟蹊径。比较常用的方式就是通过鼠标速度来模拟笔压,但今天的主角显然不是实现这种方式。
perfect-freehand
perfect-freehand是一个开源项目,借助perfect-freehand我们能够非常方便地绘制出漂亮的带压感的线条。本人之前在研究Canva的时候发现他们的画笔功能就是借助这个库实现的,并且Canva技术团队还为此发表了一篇文章:Behind the draw - How Canva's drawing tool works,感兴趣的朋友可以去阅读一下。
perfect-freehand一共提供了三个方法:getStroke、getStrokePoints和getStrokeOutlinePoints。其中核心的是getStrokePoints和getStrokeOutlinePoints,getStroke是这两个方法的组合:
我们使用getStroke这个方法就可以了,用法非常简单,将我们需要绘制的线条的数据喂给它就可以了。官方也提供了一个基于excalidraw实现的demo,这个demo比较完善,大家可自行体验一下,保证不会让你们失望的!
以下是我在这个demo里实现的一些效果😄😄:
以上效果只是为了展示perfect-freehand的带来的魔法,并不代表个人真实水平(dog)。
如何做到的
官方给出的这张动图已经很好的解释了perfect-freehand的基本运作原理:
整个过程一共两步操作,恰好对应上文所提到的两个核心方法,getStrokePoints和getStrokeOutlinePoints, 第一步是通过getStrokePoints对输入的顶点数据做一步转换,第二步再根据转换后的顶点数据计算出带“压感”的线条数据,也就是这条“轮廓”,其中算法的具体细节本人暂时还没有去研究,不过我看源码写了较多的注释,后续可以安排一下!当然,有了解过的大佬可以在评论区交流一下😘😘😘。
需要注意的
perfect-freehand带来极佳的视觉效果的同时,也带来了一些问题:
更多的内存
perfect-freehand转换后的顶点数据要比原来的数据大很多,这样对于一些需要后端存储的场景不太友好,需要考虑优化。
绘制卡顿
这个实际上是内存增大导致的,由于转换后的数据量较大,对于长笔画来说,在绘制的时候可能出现掉帧卡顿的现象。
优化
对于上述的问题,有一些比较基础的优化手段:
- 节流,可以通过节流减少输入的顶点数量,不过为了绘制流畅度,也只能使用requestAnimationFrame节流。
- 数字精度优化,perfect-freehand转换后的数据精度较高,我们可以减小精度,从而减少内存占用,这个方法比较直接。
- 使用相对坐标,将绘制点的坐标存储为相对坐标,也就是每个点的坐标表示是相对于上一个点的,这样也能减少内存占用。
除了这些优化手段,还可以使用算法优化来获取更少的数据,在上面提到的Canva发布的文章里有比较详细的介绍。
好啦,关于perfect-freehand的介绍就到这儿了!