fabric 撤销保存重做 队列+指针
画布上的 操作,撤销保存重做是少不了的,记录下实现:(项目中的撤销重做逻辑)
1、主要逻辑就是一个数组 operateList 保存所有步骤
2、每次 增删改 操作,都会将当前画布序列化,并插入当前指针 +1 项,这是防止后续可能存在步骤(而不能简单的 push 在队尾)
3、撤销,判断前一针是否有值,有值则进行反序列化,并改变指针位置 -1
4、下一步,判断后一针是否有值,有值则进行反序列化,更新指针位置 +1
5、队列长度,限制保存步数,内存有限;保存时判断是否数组过长,超长则队列从头出一个,更新指针位置 -1
代码:
import { fabric } from 'fabric'; import { Rect } from "./rect"; import { KeyCode } from "./key-code"; /** * 保存上一步下一步 * 序列化,反序列化 */ export class ActionBackNext { state = { // 指针标记当前位置 pointer: -1, // 操作记录 operateList: [], // 深度,记录步骤次数 // deep: 20, } constructor(canvas) { // 保存 canvas 对象 this.state['canvas'] = canvas; // 绑定键盘事件 this.bindKeyBoard(canvas); let rect = new fabric.Rect(Rect.defaultRect()); 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; } }); canvas.on('object:modified', (e) => { // 为了方便保存,调整图形直接触发保存 this.operateData(); console.log('save'); }); } /** * 操作保存的数据 */ operateData = () => { const { canvas, operateList, pointer, deep } = this.state; let max = deep; let list = [ ...operateList ]; // 当前状态 let currentPointer = pointer; const json = canvas.toJSON(); // 更新指针位置 currentPointer += 1; // 考虑到可能存在后续动作,插入队列操作 list.splice(currentPointer, 0, json); if (max && max < list.length) { // 深度存在,则判断当前队列长,超出则从头移出队列 list.shift(); currentPointer -= 1; } // 保存数据 this.setState({ operateList: list, pointer: currentPointer, }); } /** * 合并更新 * @param obj */ setState(obj) { this.state = Object.assign(this.state, obj); } /** * 上一步 */ prevStepOperate = () => { const { canvas, operateList, pointer } = this.state; let list = [ ...operateList ]; let currentPointer = pointer; if (currentPointer > 0) { // 加载前一步 currentPointer -= 1; canvas.loadFromJSON(list[currentPointer]).renderAll(); } this.setState({ operateList: [ ...list ], pointer: currentPointer }) } /** * 下一步 */ nextStepOperate = () => { const { canvas, operateList, pointer } = this.state; let list = [ ...operateList ]; let currentPointer = pointer; // 指针移动 currentPointer += 1; if (currentPointer >= list.length) { return; } canvas.loadFromJSON(list[currentPointer]).renderAll(); this.setState({ operateList: [ ...list ], pointer: currentPointer }); } }