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

 

posted @ 2022-04-26 14:39  名字不好起啊  阅读(645)  评论(0编辑  收藏  举报