[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);
~~~

在這種情況下,這兩個例子非常相似,大小幾乎沒有差別。但是,您可以看到使用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);
~~~

這里發生了什么?
我們在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();
~~~

注意一個非常重要的區別。使用Fabric,在嘗試“修改”任何內容之前,我們不再需要擦除內容。我們仍然使用對象,只需更改其屬性,然后重新繪制畫布即可獲得“最新圖片”。