Fabric.js利用序列化与反序列化来实作存挡功能及还原功能
2018-11-27 23:15:59
1879 次阅读
0 个评论
昨天提到利用序列化来将 canvas 变成 json 的格式,今天就来介绍如何把 JSON 格式变回画布继续让使用者使用,今天介绍完反序列化后,就接著来实作序列化和反序列实际的应用。
今天主要大纲
反序列化介绍 - loadFromJSON、loadSVGFromURL、loadSVGFromString
存挡读取功能实作
还原功能实作
反序列化 Deserialize
三种反序列化的方式
loadFromJSON - 读取 JSON 到 canvas 中
loadSVGFromURL - 使用 URL 读取 SVG
loadSVGFromString - 使用 svg path 来读取
以上 1 為在 fabric.Canvas 之下的唯一方法。
2 3 為在 fabric 底下的静态方法。
loadFromJSON
这个方法很简单就和它字面意思一样,我们只要使用 loadFromJSON 就能将我们昨天序列化出来的 json 给匯入进去了。
读取 SVG
loadSVGFromString
可以直接读取 svg string
这边使用 fabric.loadSVGFromString 来读取 SVG,后面第二个参数 callback 回传路径的物件,因為 svg 可以由很多的封闭的物件所组成,这边我们还要使用 fabric.util.groupSVGElements(objects, options) 来将所有物件群组起来,才不会散散。
loadSVGFromURL
这个 method 有跨网域限制,所以无法直接使用其他网域的 svg 档案。
使用方法和 loadSVGFromURL 只差在第一个参数是带入 URL。
实作练习
存档以及读档
这边我们简单实作存档和读档的功能
建立存档函数
这边很简单就只要将 JSON.stringify(canvas) 存在变数裡就行了。
建立读取函数
这边也很简单这要将我们储存的 saveJSON 函数,透过 canvas.loadFromJSON 读取出来就可以了~
最后在绑定事件在按钮上
我们就很轻鬆地做出存档读取的功能啦!
如果只储存在前端使用者重开存档的资料就会不见囉。
一般来说,这边我们会配合后端 API,在 save 时,透过 api 将资料储存至后端,而在 load 时去从后端读取资料。
完成程式 - https://codepen.io/nono1526/pen/OBdJye
上一步、下一步功能实作
再来我们要利用存档、读档功能,配合 canvas 的 'object:modified' 事件,来做更进阶的上一步和下一步功能。
先做上一步功能
建立一个 state 变数方便我们随时储存目前的状态。
建立 undo 為阵列型态,方便我们储存之前做过的状态。
建立 doUndo 函数,绑在按钮上
doUndo 首先要判断若 undo 是空的我们就不做任何事情。
利用 Array.pop() 函数把最后一笔的存档资料取出,再把 state 换成上一步的状态。
。
到这边我们已经简单的完成 上一步 的功能啦!
接下来我们来做 下一步 功能
先看看我们之前做的 doUndo 功能,这边為了做下一步功能,我们在更新状态前,要先把目前状态储存到 redo 阵列。
建立 doRedo 函数,其实就和 doRedo 原理都差不多。
今天主要大纲
反序列化介绍 - loadFromJSON、loadSVGFromURL、loadSVGFromString
存挡读取功能实作
还原功能实作
反序列化 Deserialize
三种反序列化的方式
loadFromJSON - 读取 JSON 到 canvas 中
loadSVGFromURL - 使用 URL 读取 SVG
loadSVGFromString - 使用 svg path 来读取
以上 1 為在 fabric.Canvas 之下的唯一方法。
2 3 為在 fabric 底下的静态方法。
loadFromJSON
这个方法很简单就和它字面意思一样,我们只要使用 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
loadSVGFromString
可以直接读取 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() })
loadSVGFromURL
这个 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 时去从后端读取资料。
完成程式 - https://codepen.io/nono1526/pen/OBdJye
上一步、下一步功能实作
再来我们要利用存档、读档功能,配合 canvas 的 'object:modified' 事件,来做更进阶的上一步和下一步功能。
先做上一步功能
建立一个 state 变数方便我们随时储存目前的状态。
建立 undo 為阵列型态,方便我们储存之前做过的状态。
// 目前状态 let state = canvas.toJSON() // 储存之前的步骤 const undo = [] 设定 canvas object:modified 事件,每当物件状态有被更新时,这时我们的 state 变数会还在更新前的状态,我们要把这个状态利用 push 储存到 undo 阵列中,最后在更新状态。 // 此事件為物件被修改后触发 canvas.on('object:modified', e => { // 把之前的状态储存 undo.push(state) // 更新状态 state = JSON.stringify(canvas) // 修改后不会有下一步储存,故 下一步 阵列清空 redo.length = 0 })
建立 doUndo 函数,绑在按钮上
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)
到这边我们已经简单的完成 上一步 的功能啦!
接下来我们来做 下一步 功能
先看看我们之前做的 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)
结果
00