Fabric.js是一個功能強大且操作簡單的Javascript HTML5 canvas工具庫。

本文主要講解 Fabric.js 有基礎也有實戰,包括:
import { fabric } from 'fabric'
在 HTML 中創建 <canvas>,並設定容器的 id 和 寬高,width/height
<canvas width="400" height="400" id="c" style="border: 1px solid #ccc;"></canvas>
這裡創建了一個 canvas 容器,邊框為灰色,id="c"。指定長寬都為 400px。

在 JS 中實例化 fabric ,之後就可以使用 fabric 的 api 管理 canvas 了。
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric' // 引入 fabric
function init() {
const canvas = new fabric.Canvas('c') // 這裡傳入的是canvas的id
// 創建一個長方形
const rect = new fabric.Rect({
top: 30, // 距離容器頂部 30px
left: 30, // 距離容器左側 30px
width: 100, // 寬 100px
height: 60, // 高 60px
fill: 'red' // 填充 紅色
})
// 在canvas畫布中加入矩形(rect)。
canvas.add(rect)
}
onMounted(() => {
init()
})
</script>

Fabric.js 的畫布操作性是非常強的。
基礎版就是“起步”章節所說的那個例子。
<template>
<canvas width="400" height="400" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
const canvas = new fabric.Canvas('canvas') // 這裡傳入的是canvas元素的id
// 創建一個長方形
const rect = new fabric.Rect({
top: 100, // 距離容器頂部 100px
left: 100, // 距離容器左側 100px
width: 30, // 矩形寬度 30px
height: 30, // 矩形高度 30px
fill: 'red' // 填充 紅色
})
canvas.add(rect) // 將矩形添加到 canvas 畫布裡
}
onMounted(() => {
init()
})
</script>
<template>
<canvas width="400" height="400" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
// 使用 StaticCanvas 創建一個不可操作的畫布
const canvas = new fabric.StaticCanvas('canvas') // 這裡傳入的是canvas元素的id
// 創建一個長方形
const rect = new fabric.Rect({
top: 100, // 距離容器頂部 100px
left: 100, // 距離容器左側 100px
width: 30, // 矩形寬度 30px
height: 30, // 矩形高度 30px
fill: 'red' // 填充 紅色
})
canvas.add(rect)
}
onMounted(() => {
init()
})
</script>
創建不可互動的畫布,其實只需把 new fabric.Canvas 改成 new fabric.StaticCanvas 即可。

const canvas = new fabric.Canvas('canvas', {
width: 300, // 畫布寬度
height: 300, // 畫布高度
backgroundColor: '#eee' // 畫布背景色
})
<template>
<canvas width="400" height="400" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
const canvas = new fabric.Canvas('canvas')
// 設定背景圖
// 參數1:背景圖資源(可以引入本地,也可以使用網路圖)
// 參數2:設定完背景圖執行以下重新渲染canvas的操作,這樣背景圖就會展示出來了
canvas.setBackgroundImage(
'圖片url',
canvas.renderAll.bind(canvas)
)
}
onMounted(() => {
init()
})
</script>
<template>
<canvas width="400" height="400" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
const canvas = new fabric.Canvas('canvas')
// fabric.Image.fromURL:加載圖片的api
// 第一個參數:圖片地址(可以是本地的,也可以是網路圖)
// 第二個參數:圖片加載的回調函數
fabric.Image.fromURL(
'圖片url',
(img) => {
// 設定背景圖
canvas.setBackgroundImage(
img,
canvas.renderAll.bind(canvas),
{
scaleX: canvas.width / img.width, // 計算出圖片要拉伸的寬度
scaleY: canvas.height / img.height // 計算出圖片要拉伸的高度
}
)
}
)
}
onMounted(() => {
init()
})
</script>
這個例子使用了 fabric.Image.fromURL 這個 api 來加載圖片,第一个參數是圖片地址,第二个參數是回調函數。
拿到圖片的參數和畫布的寬高進行計算,從而使圖片充滿全屏。

<template>
<canvas width="400" height="375" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
const canvas = new fabric.Canvas('canvas')
const rect = new fabric.Rect({
top: 100, // 距離容器頂部 100px
left: 100, // 距離容器左側 100px
fill: 'orange', // 填充 橙色
width: 100, // 寬度 100px
height: 100 // 高度 100px
})
// 將矩形添加到畫布中
canvas.add(rect)
}
onMounted(() => {
init()
})
</script>
使用 new fabric.Rect 創建 矩形。

<template>
<canvas width="400" height="375" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
const canvas = new fabric.Canvas('canvas')
const rect = new fabric.Rect({
top: 100, // 距離容器頂部 100px
left: 100, // 距離容器左側 100px
fill: 'orange', // 填充 橙色
width: 100, // 寬度 100px
height: 100, // 高度 100px
rx: 20, // x軸的半徑
ry: 20 // y軸的半徑
})
// 將矩形添加到畫布中
canvas.add(rect)
}
onMounted(() => {
init()
})
</script>
畫圓角矩形,需要添加 rx 和 ry。

<template>
<canvas width="400" height="375" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
const canvas = new fabric.Canvas('canvas')
const circle = new fabric.Circle({
top: 100,
left: 100,
radius: 50, // 圓的半徑 50
fill: 'green'
})
canvas.add(circle)
}
onMounted(() => {
init()
})
</script>
使用 new fabric.Circle 創建圓形。圓形需要使用 radius 設置半徑大小。

<template>
<canvas width="400" height="375" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
const canvas = new fabric.Canvas('canvas')
const ellipse = new fabric.Ellipse({
top: 20,
left: 20,
rx: 70,
ry: 30,
fill: 'hotpink'
})
canvas.add(ellipse)
}
onMounted(() => {
init()
})
</script>
需要使用 new fabric.Ellipse 創建 橢圓。和圓形不同,橢圓不需要設置 radius ,但要設置 rx 和 ry。
rx > ry :橢圓是橫著的rx < ry:橢圓是豎著的rx = ry: 看上去就是個圓形<template>
<canvas width="400" height="375" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
const canvas = new fabric.Canvas('canvas')
const triangle = new fabric.Triangle({
top: 100,
left: 100,
width: 80, // 底邊長度
height: 100, // 底邊到對角的距離
fill: 'blue'
})
canvas.add(triangle)
}
onMounted(() => {
init()
})
</script>
使用 new fabric.Triangle 創建三角形,三角形是需要給定 “底和高” 的。

<template>
<canvas width="400" height="375" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
const canvas = new fabric.Canvas('canvas')
const line = new fabric.Line(
[10, 10, 200, 300], // 起始點坐標
{
stroke: 'red', // 笔触顏色
}
)
canvas.add(line)
}
onMounted(() => {
init()
})
</script>
使用 new fabric.Line 創建線段。new fabric.Line 需要傳入2個參數。
stroke 。
<template>
<canvas width="400" height="375" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
const canvas = new fabric.Canvas('canvas')
const polyline = new fabric.Polyline([
{ x: 30, y: 30 },
{ x: 150, y: 140 },
{ x: 240, y: 150 },
{ x: 100, y: 30 }
], {
fill: 'transparent', // 如果畫折線,需要填充透明
stroke: '#6639a6', // 線段顏色:紫色
strokeWidth: 5 // 線段粗細 5
})
canvas.add(polyline)
}
onMounted(() => {
init()
})
</script>
使用 new fabric.Polyline 創建線段 。new fabric.Polyline 需要傳入2個參數。
需要注意的是, fill 設定成透明才會顯示成線段,如果不設置,會默認填充黑色,如下圖所示:

你也可以填充自己喜歡的顏色,new fabric.Polyline 是不會自動把 起始點 和 結束點 自動閉合起來的。

<template>
<canvas width="400" height="375" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
const canvas = new fabric.Canvas('canvas')
const polygon = new fabric.Polygon([
{ x: 30, y: 30 },
{ x: 150, y: 140 },
{ x: 240, y: 150 },
{ x: 100, y: 30 }
], {
fill: '#ffd3b6', // 填充色
stroke: '#6639a6', // 線段顏色:紫色
strokeWidth: 5 // 線段粗細 5
})
canvas.add(polygon)
}
onMounted(() => {
init()
})
</script>
使用 new fabric.Polygon 繪製多邊形,用法和 new fabric.Polyline 差不多,但最大的不同點是 new fabric.Polygon 會自動把 起始點 和 結束點 連接起來。
在Fabric.js中,幾乎所有的2d圖形直接或間接繼承自 Object 類,那麼如果我們不用其自帶的2d圖形,而是自建圖形,要怎麼應用 Fabric.js 中的方法呢?
Fabric.js 提供了 fabric.util.createClass 方法解決這個問題
一個自訂子類的結構:
// 創建一個自訂子類
const customClass = fabric.util.createClass(fabric.Object, {
type: "customClass",
initialize: function (options) {
options || (options = {});
this.callSuper("initialize", options);
// 自訂屬性
},
toObject: function () {
return fabric.util.object.extend(this.callSuper("toObject"), {
// 將自訂屬性添加到序列化物件中
});
},
_render: function (ctx) {
this.callSuper("_render", ctx);
// 自訂渲染邏輯
},
});
一個簡單的自訂類主要要修改3個地方,分別是:
此處舉一個簡單的例子,寫一個自訂表格圖形:
新增繪製網格圖的方法 initMap:
// 繪製表格圖形
function initTable(options, ctx) {
const { gridNumX, gridNumY, width, height, fill, left, top } = options;
ctx.save();
ctx.translate(-width / 2, -height / 2)
// 開始路徑並繪製線條
ctx.beginPath();
// 設定線條樣式
ctx.lineWidth = 1;
ctx.strokeStyle = fill;
// 開始繪製橫線
for (let i = 0; i < gridNumY + 1; i++) {
// 注意要算線的寬度,也就是後面那個+i
ctx.moveTo(0, height / gridNumY * i);
ctx.lineTo(width, height / gridNumY * i);
ctx.stroke();
}
// 開始繪製縱線
for (let i = 0; i < gridNumX + 1; i++) {
ctx.moveTo(width / gridNumX * i, 0);
ctx.lineTo(width / gridNumX * i, height);
ctx.stroke();
}
ctx.restore();
}
創建 Table 子類:
// 創建一個自訂子類
const Map = fabric.util.createClass(fabric.Object, {
type: "Table",
initialize: function (options) {
options || (options = {});
this.callSuper("initialize", options);
this.set("gridNumX", options.gridNumX || "");
this.set("gridNumY", options.gridNumY || "");
},
toObject: function () {
return fabric.util.object.extend(this.callSuper("toObject"), {
gridNumX: this.get("gridNumX"),
gridNumY: this.get("gridNumY"),
});
},
_render: function (ctx) {
this.callSuper("_render", ctx);
initTable({
...this
}, ctx)
},
});
新建 Table 實例並添加到canvas:
const table = new Table({
left: 100,
top: 100,
label: "test",
fill: "#faa",
width: 100,
height: 100,
gridNumX: 4,
gridNumY: 3,
});
const table2 = new Table({
left: 300,
top: 100,
label: "test",
fill: "green",
width: 200,
height: 300,
gridNumX: 2,
gridNumY: 5,
});
// 將所有圖形添加到 canvas 中
canvas.add(table, table2);
如圖所示,成功創建了可重用的自訂圖形,而且能夠使用 Object 類的功能。

Fabric.js 有3類跟文字相關的 api。
<template>
<canvas width="400" height="400" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
const canvas = new fabric.Canvas('canvas')
const text = new fabric.Text('hello')
canvas.add(text)
}
onMounted(() => {
init()
})
</script>
使用 new fabric.Text 創建文本,傳入第一個參數就是文本內容。
new fabric.Text 還支持第二個參數,可以設置文本樣式。
<template>
<canvas width="400" height="400" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
const canvas = new fabric.Canvas('canvas')
const itext = new fabric.IText('hello')
canvas.add(itext)
}
onMounted(() => {
init()
})
</script>
使用 new fabric.IText 可以創建可編輯文字,用法和 new fabric.Text 一樣。
<template>
<canvas width="400" height="400" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
const canvas = new fabric.Canvas('canvas')
const textbox = new fabric.Textbox('What are you doing?', {
width: 250
})
canvas.add(textbox)
}
onMounted(() => {
init()
})
</script>
使用 new fabric.Textbox 可以創建文本框。
new fabric.Textbox 第二個參數是對象,使用 width 可以設定了文本框的寬度,文本內容超過設定的寬度會自動換行。
new fabric.Textbox 的內容同樣是可編輯的。
<template>
<canvas width="400" height="400" id="canvas" style="border: 1px solid #ccc;"></canvas>
</template>
<script setup>
import { onMounted } from 'vue'
import { fabric } from 'fabric'
function init() {
const canvas = new fabric.Canvas('canvas')
const circle = new fabric.Circle({
top: 100,
left: 100,
radius: 50, // 半徑:50px
backgroundColor: 'green', // 背景色:綠色
fill: 'orange', // 填充色:橙色
stroke: '#f6416c', // 邊框顏色:粉色
strokeWidth: 5, // 邊框粗細:5px
strokeDashArray: [20, 5, 14], // 邊框虛線規則:填充20px 空5px 填充14px 空20px 填充5px ……
shadow: '10px 20px 6px rgba(10, 20, 30, 0.4)', // 投影:向右偏移10px,向下偏移20px,羽化6px,投影顏色及透明度
transparentCorners: false, // 選中時,角是被填充了。true 空心;false 實心
borderColor: '#16f1fc', // 選中時,邊框顏色:天藍
borderScaleFactor: 5, // 選中時,邊的粗細:5px
borderDashArray: [20, 5, 10, 7], // 選中時,虛線邊的規則
cornerColor: "#a1de93", // 選中時,角的顏色是 青色
cornerStrokeColor: 'pink', // 選中時,角的邊框的顏色是 粉色
cornerStyle: 'circle', // 選中時,叫的屬性。默認rect 矩形;circle 圓形
cornerSize: 20, // 選中時,角的大小為20
cornerDashArray: [10, 2, 6], // 選中時,虛線角的規則
selectionBackgroundColor: '#7f1300', // 選中時,選框的背景色:朱紅
padding: 40, // 選中時,選擇框離元素的內邊距:40px
borderOpacityWhenMoving: 0.6, // 當對象活動和移動時,對象控制邊界的不透明度
})
canvas.add(circle)
}
onMounted(() => {
init()
})
</script>