昨天提到利用序列化來將 canvas 變成 json 的格式,今天就來介紹如何把 JSON 格式變回畫布繼續讓使用者使用,今天介紹完反序列化後,就接著來實作序列化和反序列實際的應用。
今天主要大綱
三種反序列化的方式
fabric.Canvas
之下的唯一方法。fabric
底下的靜態方法。這個方法很簡單就和它字面意思一樣,我們只要使用 loadFromJSON 就能將我們昨天序列化出來的 json 給匯入進去了。
const save = '{"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"}'
const canvas = new fabric.Canvas('canvas')
canvas.loadFromJSON(save)
可以直接讀取 svg string
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
</svg>
這邊使用 fabric.loadSVGFromString
來讀取 SVG,後面第二個參數 callback
回傳路徑的物件,因為 svg 可以由很多的封閉的物件所組成,這邊我們還要使用 fabric.util.groupSVGElements(objects, options)
來將所有物件群組起來,才不會散散。
fabric.loadSVGFromString(SVGString, (objects, options) => {
const obj = fabric.util.groupSVGElements(objects, options)
canvas.add(obj).renderAll()
})
這個 method 有跨網域限制,所以無法直接使用其他網域的 svg 檔案。
使用方法和 loadSVGFromURL
只差在第一個參數是帶入 URL。
這邊我們簡單實作存檔和讀檔的功能
JSON.stringify(canvas)
存在變數裡就行了。function save () {
saveJSON = JSON.stringify(canvas)
alert('save canvas!')
textarea.innerHTML = saveJSON
}
saveJSON
函數,透過 canvas.loadFromJSON
讀取出來就可以了~function load () {
alert('load canvas!')
textarea.innerHTML = ''
canvas.loadFromJSON(saveJSON)
}
saveBtn.addEventListener('click', save)
loadBtn.addEventListener('click', load)
我們就很輕鬆地做出存檔讀取的功能啦!
結果
如果只儲存在前端使用者重開存檔的資料就會不見囉。
一般來說,這邊我們會配合後端 API,在 save
時,透過 api 將資料儲存至後端,而在 load
時去從後端讀取資料。
再來我們要利用存檔、讀檔功能,配合 canvas 的 'object:modified'
事件,來做更進階的上一步和下一步功能。
state
變數方便我們隨時儲存目前的狀態。// 目前狀態
let state = canvas.toJSON()
// 儲存之前的步驟
const undo = []
object:modified
事件,每當物件狀態有被更新時,這時我們的 state 變數會還在更新前的狀態,我們要把這個狀態利用 push
儲存到 undo
陣列中,最後在更新狀態。// 此事件為物件被修改後觸發
canvas.on('object:modified', e => {
// 把之前的狀態儲存
undo.push(state)
// 更新狀態
state = JSON.stringify(canvas)
// 修改後不會有下一步儲存,故 下一步 陣列清空
redo.length = 0
})
doUndo
首先要判斷若 undo 是空的我們就不做任何事情。Array.pop()
函數把最後一筆的存檔資料取出,再把 state
換成上一步的狀態。。
function doUndo () {
if (undo.length) {
alert('目前沒有動作可復原')
return
}
// 取出 undo 最後一筆資料讀取
let lastJSON = undo.pop()
canvas.loadFromJSON(lastJSON)
// 換成上一步的狀態
state = lastJSON
}
undoBtn.addEventListener('click', doUndo)
到這邊我們已經簡單的完成 上一步
的功能啦!
接下來我們來做 下一步
功能
redo
陣列。function doUndo () {
...略
// 在做上一步時把目前狀態 push 到 redo 陣列
redo.push(state)
// 換成目前的狀態
state = lastJSON
}
doRedo
函數,其實就和 doRedo
原理都差不多。function doRedo () {
if (!redo.length) {
alert('目前沒有動作可復原')
return
}
let lastJSON = redo.pop()
canvas.loadFromJSON(lastJSON)
// 在做下一步時把目前
狀態 push 到 undo 陣列
undo.push(state)
// 換成目前的狀態
state = lastJSON
}
redoBtn.addEventListener('click', doRedo)
結果
██████████████████████████████████████
高雄卡訊電子 徵才 | UI 設計師
██████████████████████████████████████
哈囉,我們是高雄的卡訊電子,做麥克風、會議系統的。目前需要一位設計師來協助產品的UI介面設計。
【工作內容】
與前端工程師協作,開發 web 操作介面。
1.基本條件
有美感,會做UI,願意溝通討論,履歷記得附上作品集。
2.公司提供 Adobe CC、Axure、雙 24” 繪圖螢幕、Wacom Intuos Pro 繪圖板.
【月薪區間】
3萬~4萬5
訪問前設計師對這份工作的想法:
【優點】
與他合作的工程師有一些 sense
只要在 Design guideline 下大致上是開放的
只要能說服老闆,設計是會被採用的。(老闆不會直接跟你說no)
【缺點】
對設計師來說這裡的產品比較不多元,都是操作介面
詳細徵才資訊:https://www.104.com.tw/job/?jobno=5buv5
104 上官方列的基本條件,沒有很重要:
真正重要的是上面第1點,只要有美感,會做UI,可以溝通即可。
安捏姆湯喔