今天我們要來了解 Fabricjs 的序列化功能。一般我們會在什麼情境下用到這個功能呢?
就是存檔和讀檔啦!
使用者若有存取和讀取自己所設計的圖片的需求,我們會利用將 canvas 中的內容整個序列化,變成 json 的格式,當使用者想再次修改時,就從後端把之前做過序列化的資料讀取出來,被且再透過 fabricjs 的解析,又變成一個能夠自由修改的畫布啦!。
另外 fabricjs 還提供了 canvas 轉成 svg path 的序列化,也相當的實用。
今天就主要介紹幾種序列化的方法
我們能夠輕鬆地將 canvas 序列化成 json 的格式,這邊有兩種方式。
toJSON() 方法也是回傳 javascript 物件,所以必須要使用
JSON.stringify(canvas.toJSON)
才能拿到 json 字串呦!
我們先什麼都不做直接將 canvas 給序列化
const canvas = new fabric.Canvas('canvas')
console.log(JSON.stringify(canvas))
我們知道其實 JSON 格式就是 javascript 的物件變成用字串的方式組合。
這邊把序列化後的資料透過 console
出來後,我的得到了 fabricjs 的版本以及一個空的 objects
陣列:
{"version":"2.4.1","objects":[]}
因為我們什麼都還沒有加入,所以我們可以猜測 objects
這個物件會放之後 add
進 canvas 裏頭的物件。
在來加入背景顏色和一個矩形後再來序列化
{"version":"2.4.1","objects":[{"type":"rect","version":"2.4.1","originX":"left","originY":"top","left":0,"top":0,"width":100,"height":100,"fill":"rgb(0,0,0)","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":4,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"rx":0,"ry":0}],"background":"#222"}
哇!我們不過加入了一個矩形而已,資料竟然就暴增了這麼多,不過 objects
物件確實就是裝著被我們 add
進 canvas 中的 rect 物件。
這邊可以看到我有用粗體標記的部分
"type":"rect"
裝在 objects
中,也就是我們新增出來的矩形。
"background:"#222"
這個就是我們幫 canvas 上的背景顏色,也被儲存了起來。
toJSON 不會直接回傳 json string,必須要自己使用
JSON.stringiy()
不過我自己嘗試了以後發現,toObject 和 toJSON 都回傳了 javascript Object
console.log(canvas.toObject())
console.log(canvas.toJSON())
得到
後來在 fabric 原始碼發現
所以其實是一樣的?
好吧~那這邊主要就來用講解一下 toObject
吧
其實不只 canvas 可以呼叫 toObject
,其實在 fabric.Object
也有 toObject
方法,而 canvas 的 toObject
就是把所有底下的物件都呼叫 toObject
並且儲存起來到序列化中的 objects
物件之下。
這邊我們可以做個實驗。
我們偷偷修改 rect
的 toObject
的方法。並在一次做序列化,來查看資料是否有變。
// 偷偷增加在 rect 中變更 toObject 方法
rect.toObject = function () {
return {
custom: true,
sayHi: 'Halo'
}
}
{"version":"2.4.1","objects":[{"custom":true,"sayHi":"Halo"}],"background":"#222"}
發現資料被修改囉!
今天我們可能想為了我們所新增的物件,添加自己的屬性,若直接使用序列化是無法被編寫上去的,這時只要透過我們剛剛學到的: 修改物件的 toObject
方法。
我們可以透過更改物件上 toObject
方法,來讓物件在序列化時,也儲存我們的自訂屬性。
假設今天我們想為我們的 rect
加上 name 屬性
rect.name = 'Nono'
這時直接使用序列化是不行的,會發現序列化後沒有這個屬性。
透過:
rect.toObject = (function(toObject) {
return function() {
return fabric.util.object.extend(toObject.call(this), {
name: this.name
})
}
})(rect.toObject)
這邊比較複雜一些,稍微看了一下,大概的作法就是將 toObject
先呼叫一次取得目前的 object 後讓兩個物件合併起來。
{"version":"2.4.1","objects":[{"type":"rect","version":"2.4.1","originX":"left","originY":"top","left":0,"top":0,"width":100,"height":100,"fill":"rgb(0,0,0)","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeLineJoin":"miter","strokeMiterLimit":4,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"clipTo":null,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","transformMatrix":null,"skewX":0,"skewY":0,"rx":0,"ry":0,"name":"Nono"}],"background":"#222"}
這樣一來我們就能將我們的自訂屬性正常的序列化囉!
我們可以簡單的直接將 fabricjs 的物件轉成 svg。
透過:
rect.toSVG()
簡單又快速的就把 canvas 轉成 svg 囉
這裡必須要注意到 fabricjs 主要是能夠序列化 物件
,所以要記得事件是不會一起被序列化的喔!
下面這個不會被序列化喔
我們在一次加入事件後,在做序列化
canvas.on('mouse:move', (e) => {
console.log(e)
})
這時使用序列化也不會將事件儲存起來喔!
把 canvas 序列化變成
並且了解 fabricjs 序列化的深入用法: 自訂屬性
明天再來使用反序列化把畫布還原。