Fabric.js利用序列化与反序列化来实作存挡功能及还原功能

2018-11-27 23:15:59
1721次阅读
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 给匯入进去了。

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

登录 后评论。没有帐号? 注册 一个。