JS前端以轻量fabric.js实现示例理解canvas
作者:尤水就下
缘起
最近想系统看下 canvas 这个东西,所以找了一个库看看,本来打算写一两篇文章沉淀一下,发现东西有点多😦,索性就拆成了系列文章,应该有十几章吧,目录大概是下面这个样子:
目前的话已经写了 80% 左右的草稿,接下来会每周重新梳理一篇发出来。
canvas 这个东西其实没太多很好的资料能够系统提升,大多都是 api 的教程(可以用来入门)和某些效果的实现(看完直呼卧槽,隔天就忘了),会有种支离破碎的感觉🤨,很难互相关联起来。
我之前想学这方面的东西也很是迷茫,不知道从哪里下手,不过 fabric.js 这个库确实能够帮你打开 canvas 世界的大门。
如果你想往可视化这个方向靠拢,这个系列一定能够对你有所帮助。
fabric.js 初体验
canvas 很强大,能够让我们创建一些令人惊叹的图形和功能,不过它仅仅是用来绘制而已,如果要进行一些交互或者改变画布上的内容,就是一件很麻烦的事情😬。好在 fabric.js 为 canvas 提供了一个缺失的对象模型,它负责管理画布的状态和渲染,可以让我们直接使用对象来操纵画布,极大方便了开发。如果没用过 fabric.js 的同学建议大家去官网看一下,体验一下效果,这里放张动图让大家感受一下:
这个系列讲的就是如何一步一步从头实现上面的效果,麻雀虽小五脏俱全。虽然讲的是 fabric.js 的实现,但其实是 canvas 的知识,学完这个系列再去看其他 canvas 库应该能更加得心应手。
好的😬,显然有同学没有去试一下 fabric.js,所以这里我们简单看一下它的使用方式:
// 第一步:初始化画布 const canvas = new fabric.Canvas('canvas'); // 第二步:创建物体 const rect = new fabric.Rect({ top: 200, left: 100, width: 100, height: 100, fill: 'green', }); // 第三步:向画布中添加物体,调用 add 才会绘制到画布中 canvas.add(rect); // 第四步:让物体动起来 rect.animate( { top: 50, left: 400, angle: 45 }, { duration: 1000, onChange: canvas.renderAll.bind(canvas) } );
只要上面少许的代码,就能在 canvas 中画出一个绿色矩形,并且让它动起来,此外还默认支持三种变换操作(平移、旋转、缩放),非常适用于一些编辑和交互类的场景。
fabric.js 的大体结构
那 fabric.js 是个啥呢,我们引入这个库之后可以在控制台打印一下 fabric:
会看到 fabric(就是命名空间)下挂载这一堆东西(各种类等),我们再截个源码的图:
和控制台打印的结果大体能匹配上,fabric.js 的源码写的还是很清楚的,几乎每个文件都是一个类,层次分明,注释也多(不过是英文的),通过名字你也能猜测这个类是干什么的。我们则会实现它的核心部分并讲解一些具体思想,大概是下面这个样子:
这里给个最终实现版本的效果图:
如果你没接触过类似的东西,肯定会有很多困惑😴,比如:
- 怎么判断物体被点选和框选
- 变换操作怎么搞
- 动画怎么弄
- 怎么分组
- 如何扩展
- 要啥数学知识
- ... 那就让我们带着问题一起学习吧🚗。
canvas 能干嘛?
鉴于有些同学 canvas 接触的比较少,所以这里就简单总结和回顾下 canvas 的一些基础知识。事实上 canvas 的 api 并不多,就像 html 中的标签,常用的就那么几个,但你没碰过就会总觉得它很多又不好学,其实我们只要知道它能做什么就行🤔:
- 它能画圆弧、矩形、图片、文本、线段、贝塞尔曲线、svg
- 画的时候可以设置各种属性,主要有线宽、填充色、描边色 没了,你只要记住这两点就行,剩下的全靠想象,比如:
- 你要画一个圆就用圆弧来画
- 你要画一个点就用一个很小的圆来画
- 你要画一个多边形,那就用多条线段相连
- 你要画一个很长的曲线,那就用多段贝塞尔曲线相连,或者用多段很小很小的线段以直代曲
- 你要画个图表,那就得一笔一笔慢慢画,比如雷达图就是一堆圆圈和一个多边形组成
- 当然最好配合上图片,这样能让 canvas 看起来更加多姿多彩
- ... 另外你还要知道 canvas 虽然是个标签,但它其实就是一幅画,画完的东西是不可改的,只能覆盖或者重新画;也无状态,canvas 并不知道你画的是啥,也不知道你鼠标点的是哪个物体,要是想稍微修改下画布上面物体的大小或者让物体动起来,那一定是要清除(整个或局部)画布再重新绘制的。 然后再说几个注意事项:
- 画新的几何图形和线条之前最好都要调用一下 beginPath,不然前后两个图形可能会相互影响,这是个好习惯。
- save() 和 restore() 一定要配对使用,这也是个好习惯。
- 善于运用 transform 的三种变换,以后的章节会讲解到。
这里简单扩充下 save 和 restore 这个知识点,因为一开始我们可能会对这个 api 的用法比较迷茫。一般来说 canvas 上面的东西是一个一个绘制的,每个东西有自己的状态,以 lineWidth(线宽) 和 strokeStyle(描边色) 为例子:
const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); // ... 这里如果有绘制东西,用的是默认值 ctx.save(); // ---------①开始----------↓ ctx.lineWidth = 1; ctx.strokeStyle = 'red'; ctx.strokeRect(0, 0, 20, 20); ctx.save(); // ----------②开始---------↓ ctx.lineWidth = 2; ctx.strokeStyle = 'green'; ctx.strokeRect(20, 20, 20, 20); ctx.save(); // ----------③开始----------↓ ctx.lineWidth = 3; ctx.strokeStyle = 'blue'; ctx.strokeRect(40, 40, 20, 20); // ... 这里如果有绘制东西,用的是③的状态 ctx.restore(); // ---------③结束----------↑ // ... 这里如果有绘制东西,用的是②的状态 ctx.restore(); // ----------②结束---------↑ // ... 这里如果有绘制东西,用的是①的状态 ctx.restore(); // ----------①开始---------↑ // ... 这里如果有绘制东西,用的是默认值
效果大概是下面这个样子:
你可以把 save 和 restore 当做一对花括号🤯,所以它们得成对出现,每个括号里面是一种新的绘制状态,括号外面又是另一种状态。
它的本质是栈的结构(就是只能尾部添加或删除的数组),save 的时候会把当前的一堆状态属性保存起来(就是一个大对象),restore 的时候就是将栈顶的保存的状态拿出来置为当前的状态,看看下面这张图应该能比较好的理解👇🏻:
我自己觉得 canvas 的库大体都是数据驱动视图的一种很好体现,我们只负责单纯的修改数据(物体的各种属性状态值),canvas 只负责单纯的绘制,两者各司其职,互不干扰,也有点单向数据流的味道。
小结
这个章节主要简单介绍了下 fabric.js 和 canvas,这是这个系列的第一篇文章,其它文章也在路上了,暂时先把简版 fabric.js 的代码链接放出来吧,不过目前还在整理中
以上就是JS前端以轻量fabric.js实现示例理解canvas的详细内容,更多关于JS前端轻量fabric.js canvas的资料请关注脚本之家其它相关文章!