[Canvas](http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html)可以让我们在网络上创造出绝对惊人的图形。但它提供的API是令人失望的。如果我们只想在画布上画几条基本的形状,不会觉得有什么繁琐。但是一旦需要任何形式的互动,任何时候改变图片或绘制更复杂的形状,代码复杂度会急剧增加。
Fabric旨在解决这个问题。
原生canvas方法只允许我们触发简单的图形命令,盲目的修改canvas的位图,想画一个矩形?使用`fillRect(left, top, width, height).`,想画一条线?使用`moveTo(left, top)`和`lineTo(x, y)`的组合命令,就好像我们**用刷子画画**,上层涂上越来越多的颜料,几乎没有控制。
Fabric不是在这么低的层次上运行,而是在原生方法之上提供简单而强大的对象模型。它负责画布状态和渲染,并让我们直接使用绘制后的“对象”。
让我们来看一个简单的例子来说明这个差异。假设我们想在画布上画一个红色的矩形。以下是我们如何使用原生的`<canvas>`API。
~~~js
// 有一个id是c的canvas元素
var canvasEl = document.getElementById('c');
// 获取2d位图模型
var ctx = canvasEl.getContext('2d');
// 设置填充颜色
ctx.fillStyle = 'red';
// 创建一个坐标100,190,尺寸是20,20的矩形
ctx.fillRect(100, 100, 20, 20);
~~~
现在使用Fabric做同样的事情:
~~~js
// 用原生canvas元素创建一个fabric实例
var canvas = new fabric.Canvas('c');
// 创建一个矩形对象
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'red',
width: 20,
height: 20
});
// 将矩形添加到canvas画布上
canvas.add(rect);
~~~
![](https://img.kancloud.cn/af/a9/afa9372fd091c7ab541021dba345854c_209x209.png)
在这种情况下,这两个例子非常相似,大小几乎没有差别。但是,您可以看到使用canvas的方法有多么不同。使用原生方法,我们在**上下文**中操作(表示整个画布位图的对象),在Fabric中,我们操作对象,实例化它们,更改其属性,并将其添加到画布。你可以看到这些对象是Fabric中的**第一等公民**。
但渲染纯正的红色矩形就如此无聊。我们至少可以做一些有趣的事情!也许,稍稍旋转?
旋转45度,首先使用原生的canvas方法:
~~~js
var canvasEl = document.getElementById('c');
var ctx = canvasEl.getContext('2d');
ctx.fillStyle = 'red';
ctx.translate(100, 100);
ctx.rotate(Math.PI / 180 * 45);
ctx.fillRect(-10, -10, 20, 20);
~~~
使用Fabric:
~~~js
var canvas = new fabric.Canvas('c');
// 创建一个45度的矩形
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'red',
width: 20,
height: 20,
angle: 45
});
canvas.add(rect);
~~~
![](https://img.kancloud.cn/cc/bc/ccbc3fa69333db0220ad271aa68c2e16_208x209.png)
这里发生了什么?
我们在Fabric中所做的一切都是将对象的“角度”值更改为`45`。然而使用原生的方法,事情变得更加有趣,记住我们无法对对象进行操作,相反,我们调整整个画布位图(`ctx.translate`,`ctx.rotate`)的位置和角度,以适应我们的需要。然后,我们再次绘制矩形,但记住要正确地偏移位图(-10,-10),所以它仍然呈现在100,100点。作为练习,我们不得不在旋转画布位图时将度数转换为弧度。
我相信你刚刚开始明白为什么面料存在,以及它解决了多少低级写法。
如果在某些时候,我们想将现在熟悉的红色矩形移动到画布上稍微不同的位置怎么办?我们如何在无法操作对象的情况下执行此操作?我们会在canvas位图上调用另一个`fillRect`吗?
不完全的。调用另一个`fillRect`命令实际上在画布上绘制的东西所有之上绘制矩形。还记得我前边说的用刷子画画吗?为了“移动”它,我们需要先**擦除以前绘制的内容**,然后在新的位置绘制矩形。
~~~js
var canvasEl = document.getElementById('c');
...
ctx.strokRect(100, 100, 20, 20);
...
// 擦除整个画布
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
ctx.fillRect(20, 50, 20, 20);
~~~
我们如何用Fabric完成这个?
~~~js
var canvas = new fabric.Canvas('c');
...
canvas.add(rect);
...
rect.set({ left: 20, top: 50 });
canvas.renderAll();
~~~
![](https://img.kancloud.cn/82/6b/826bbf90e8e5457c74893887270e5eff_208x208.png)
注意一个非常重要的区别。使用Fabric,在尝试“修改”任何内容之前,我们不再需要擦除内容。我们仍然使用对象,只需更改其属性,然后重新绘制画布即可获得“最新图片”。