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 ],
});
}
}

浙公网安备 33010602011771号