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

 

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