perfect-freehand:给你的svg&canvas线条加点“力”

639 阅读3分钟

使用canvas&svg绘制线条

使用过svg或者canvas来绘制线条的朋友应该比较清楚,在默认情况下,绘制出的线条粗细是均匀的,如下图所示:

未命名.gif

你也可以点击这里尝试一下

使用均匀的线条能实现的效果有限,例如,无法实现艺术字这样的效果。

那么,我们如何绘制出这种带有“压感”的线条呢?

对于支持压感的硬件设备来说,通过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转换后的顶点数据要比原来的数据大很多,这样对于一些需要后端存储的场景不太友好,需要考虑优化。

绘制卡顿

这个实际上是内存增大导致的,由于转换后的数据量较大,对于长笔画来说,在绘制的时候可能出现掉帧卡顿的现象。

优化

对于上述的问题,有一些比较基础的优化手段:

  1. 节流,可以通过节流减少输入的顶点数量,不过为了绘制流畅度,也只能使用requestAnimationFrame节流。
  2. 数字精度优化,perfect-freehand转换后的数据精度较高,我们可以减小精度,从而减少内存占用,这个方法比较直接。
  3. 使用相对坐标,将绘制点的坐标存储为相对坐标,也就是每个点的坐标表示是相对于上一个点的,这样也能减少内存占用。

除了这些优化手段,还可以使用算法优化来获取更少的数据,在上面提到的Canva发布的文章里有比较详细的介绍。

好啦,关于perfect-freehand的介绍就到这儿了!