使用Fabric.js玩轉canvas

前言
之前使用這個框架寫過一個卡片DIY的專案,中間遇到很多問題都只能通過google或github issues才能解決,國內資料較少,所以才想寫這篇文章來簡單的做下總結,希望可以幫到其他人哈。
附上個人專案地址: vue-card-diy 歡迎star~ :sparkles:
什麼是Fabric.js?
Fabric.js 是一個強大的H5 canvas框架,在原生canvas之上提供了互動式物件模型,通過簡潔的api就可以在畫布上進行豐富的操作。
該框架是個開源專案,專案地址: github
Fabric.js有什麼功能?
使用Fabric.js,你可以在畫布上建立和填充物件; 比如簡單的幾何形狀 - 矩形,圓形,橢圓形,多邊形,自定義圖片或由數百或數千個簡單路徑組成的更復雜的形狀。 另外,還可以使用滑鼠縮放,移動和旋轉這些物件; 修改它們的屬性 - 顏色,透明度,z-index等。也可以將畫布上的物件進行組合。下面我將會介紹我常用的功能以及場景,更多功能可以參考官方文件
安裝
npm安裝
npm install fabric --save 複製程式碼
通過cdn引用
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.6/fabric.min.js"></script> 複製程式碼
初始化
首先在html頁面中寫一個350 x 200的canvas標籤, 這裡不寫寬高也行,後面可以通過js來設定寬高
<canvas id="canvas" width="350" height="200"></canvas> 複製程式碼
初始化fabric的canvas物件,建立一個卡片(後面都用 card
表示畫布物件)
const card = new fabric.Canvas('canvas') // ...這裡可以寫canvas物件的一些配置,後面將會介紹 // 如果<canvas>標籤沒設定寬高,可以通過js動態設定 card.setWidth(350) card.setHeight(200) 複製程式碼
就是這麼簡單,這樣就建立了一個基本的畫布。
開始花樣操作
監聽畫布上的事件
官方提供了很多事件,以下為常用的事件:
object:added object:modified object:removed selection:created selection:updated selection:cleared
// 在canvas物件初始化後,通過以下方式監聽 // 比如監聽畫布的圖層編輯事件 card.on('object:modified', (e) => { console.log(e.target) // e.target為當前編輯的Object // ...旋轉,縮放,移動等編輯圖層的操作都監聽到 // 所以如果有撤銷/恢復的場景,這裡可以儲存編輯狀態 }); 複製程式碼
設定畫布背景

// 讀取圖片地址,設定畫布背景 fabric.Image.fromURL('xx/xx/bg.jpg', (img) => { img.set({ // 通過scale來設定圖片大小,這裡設定和畫布一樣大 scaleX: card.width / img.width, scaleY: card.height / img.height, }); // 設定背景 card.setBackgroundImage(img, card.renderAll.bind(card)); card.renderAll(); }); 複製程式碼
如果要設定畫布的背景顏色,可以在canvas初始化時設定
const card = new fabric.Canvas('canvas', { backgroundColor: 'blue' // 畫布背景色為藍色 }); // 或者 card.backgroundColor = 'blue'; // 或者 card.setBackgroundColor('blue'); 複製程式碼
向畫布新增圖層物件
fabric.js提供了很多物件,除了基本的 Rect
, Circle
, Line
, Ellipse
, Polygon
, Polyline
, Triangle
物件外,還有如 Image
, Textbox
, Group
等更高階的物件,這些都是繼承自Fabric的Object物件。
下面我就介紹如何新增圖片和文字,其他物件大同小異
/** * 如何向畫布新增一個Image物件? */ // 方式一(通過img元素新增) const imgElement = document.getElementById('my-image'); const imgInstance = new fabric.Image(imgElement, { left: 100, // 圖片相對畫布的左側距離 top: 100, // 圖片相對畫布的頂部距離 angle: 30, // 圖片旋轉角度 opacity: 0.85, // 圖片透明度 // 這裡可以通過scaleX和scaleY來設定圖片繪製後的大小,這裡為原來大小的一半 scaleX: 0.5, scaleY: 0.5 }); // 新增物件後, 如下圖 card.add(imgInstance); 複製程式碼

// 方式二(通過圖片路徑新增) fabric.Image.fromURL('xx/xx/vue-logo.png', (img) => { img.set({ hasControls: false, // 是否開啟圖層的控制元件 borderColor: 'orange', // 圖層控制元件邊框的顏色 }); // 新增物件後, 如下圖 canvas.add(img); }); 複製程式碼

/** * 如何向畫布新增一個Textbox物件? */ const textbox = new fabric.Textbox('這是一段文字', { left: 50, top: 50, width: 150, fontSize: 20, // 字型大小 fontWeight: 800, // 字型粗細 // fill: 'red', // 字型顏色 // fontStyle: 'italic',// 斜體 // fontFamily: 'Delicious', // 設定字型 // stroke: 'green', // 描邊顏色 // strokeWidth: 3, // 描邊寬度 hasControls: false, borderColor: 'orange', editingBorderColor: 'blue' // 點選文字進入編輯狀態時的邊框顏色 }); // 新增文字後,如下圖 card.add(textbox); 複製程式碼

獲取當前選中的圖層物件
// 方式一 this.selectedObj = card.getActiveObject(); // 返回當前畫布中被選中的圖層 // 方式二 card.on('selection:created', (e) => { // 選中圖層事件觸發時,動態更新賦值 this.selectedObj = e.target }) 複製程式碼
旋轉圖層
// 順時針90°旋轉 const currAngle = this.selectedObj.angle; // 當前圖層的角度 const angle = currAngle === 360 ? 90 :currAngle + 90; this.selectedObj.rotate(angle); // 如果是通過滑塊的方式控制旋轉 // this.selectedObj.rotate(slideValue); // 所有圖層的操作之後,都需要呼叫這個方法 card.renderAll() 複製程式碼
翻轉圖層
// 水平翻轉,同理垂直翻轉改為scaleY屬性 this.selectedObj.set({ scaleX: -this.selectedObj.scaleX, }) card.renderAll() 複製程式碼
移除圖層
card.remove(this.selectedObj) // 傳入需要移除的object card.renderAll() 複製程式碼
控制畫布上的圖層層級
向畫布新增圖層,預設是依次往上疊加,但是當你選中一個圖層進入 active
狀態時,該圖層會預設置於頂層,如果像禁止選中圖層時指定,可以:
// 在畫布初始化後設置 card.preserveObjectStacking = true // 禁止選中圖層時自定置於頂部 複製程式碼
設定之後,我選中vue logo就是這個樣子,不會置頂。

如何上移和下移圖層?
// 上移圖層 this.selectedObj.bringForward(); // 下移圖層 this.selectedObj.sendBackwards(); // 也可以使用canvas物件的moveTo方法,移至圖層到指定位置 card.moveTo(object, index); 複製程式碼
畫布狀態記錄
框架提供瞭如 toJSON
和 loadFromJSON
方法,作用分別為匯出當前畫布的json資訊,載入json畫布資訊來還原畫布狀態。
// 匯出當前畫布資訊 const currState = card.toJSON(); // 匯出的Json如下圖 複製程式碼

// 載入畫布資訊 card.loadFromJSON(lastState, () => { card.renderAll(); }); 複製程式碼
將畫布匯出成圖片
const dataURL = card.toDataURL({ format: 'jpeg', // jpeg或png quality: 0.8 // 圖片質量,僅jpeg時可用 // 擷取指定位置和大小 //left: 100, //top: 100, //width: 200, //height: 200 }); 複製程式碼