fabric 撤销保存重做 - 2个队列
之前做了个用指针标记位置的,换了个思路用2个队列实现一下:(类似浏览器的前进后退逻辑,撤销后重做,会丢弃撤销前的步骤)
1、2个数组保存所有步骤: 历史队列 previous,未来队列 future,画布内容永远为 previous 队列最后一个值
2、每次 增删改 操作,都会将当前画布序列化,并压入 previous,
3、撤销,判断 previous,有值则尾部出栈,将出栈元素压入 future
4、下一步,判断 future,有值则头部移除队列,压入 previous
代码记录:
import { fabric } from 'fabric'; import { Rect } from "./rect"; import { KeyCode } from "./key-code"; /** * 保存上一步下一步 * 类似浏览器的前进后退逻辑,撤销后重做,会丢弃撤销前的步骤 * 1、2个数组保存所有步骤: 历史队列 previous,未来队列 future,画布内容永远为 previous 队列最后一个值 * 2、每次 增删改 操作,都会将当前画布序列化,并压入 previous, * 3、撤销,判断 previous ,有值则尾部出栈,将出栈元素压入 future * 4、下一步,判断 future ,有值则头部移出队列,压入 previous * 5、队列长度,限制保存步数,内存有限;保存时判断是否数组过长,超长则 previous 栈头部出栈一个值 */ export class ActionBackNext2 { state = { // 历史队列 previous: [], // 未来队列 future: [], // 深度,记录步骤次数 // deep: 20, } constructor(canvas) { //override prototype.toObject and add your custom properties here fabric.Object.prototype.toObject = (function (toObject) { return function () { return fabric.util.object.extend(toObject.call(this), { name: this.name, selectable: this.selectable, }); }; })(fabric.Object.prototype.toObject); // 保存 canvas 对象 this.state['canvas'] = canvas; // 绑定键盘事件 this.bindKeyBoard(canvas); // selectable 测试序列化,反序列化失效问题 - 结论是没问题 let data = Object.assign(Rect.defaultRect(), { name: 1, selectable: true }); let rect = new fabric.Rect(data); canvas.add(rect); // 添加也要保存 this.operateData(); } /** * 绑定键盘事件 * @param canvas */ bindKeyBoard(canvas) { $(document).on('keydown', (e) => { const key = e.originalEvent.keyCode; switch (key) { // case KeyCode.Q: // 保存 // console.log('save'); // this.operateData(); // break; case KeyCode.W: // 上一步 console.log('back'); this.prevStepOperate(); break; case KeyCode.E: // 下一步 console.log('next'); this.nextStepOperate(); break; case KeyCode.R: // 打印,测试自增属性的 序列化反序列化 console.log(canvas.getObjects()); break; } }); canvas.on('object:modified', (e) => { // 为了方便保存,调整图形直接触发保存 this.operateData(); console.log('save'); }); } /** * 操作保存的数据 */ operateData = () => { const { canvas, previous, deep } = this.state; let max = deep; let prev = [ ...previous ]; const json = canvas.toJSON(); // 入栈 prev prev.push(json); if (max && max < prev.length) { // 深度存在,则判断 previous 长度,超出则从头出栈 prev.shift(); } // 保存数据 this.setState({ previous: prev, // 清空未来队列,当前存在为最新 future: [], }); } /** * 合并更新 * @param obj */ setState(obj) { this.state = Object.assign(this.state, obj); } /** * 上一步 */ prevStepOperate = () => { const { canvas, previous, future } = this.state; let prev = [ ...previous ], fut = [ ...future ]; if (prev.length <= 1) { // prev 只剩最后一值,已到最前 return false; } // prev 出栈,压入 fut fut.push(prev.pop()); // 设置 prev 最后一值为当前画布内容 canvas.loadFromJSON(prev[prev.length - 1]).renderAll(); this.setState({ previous: [ ...prev ], future: [ ...fut ], }); } /** * 下一步 */ nextStepOperate = () => { const { canvas, previous, future } = this.state; let prev = [ ...previous ], fut = [ ...future ]; if (fut.length === 0) { // fut 无值,已到最新 return false; } // fut 出栈,压入 undo prev.push(fut.pop()); // 设置 prev 最后一值为当前画布内容 canvas.loadFromJSON(prev[prev.length - 1]).renderAll(); this.setState({ previous: [ ...prev ], future: [ ...fut ], }); } }