Javascript Fabricjs-在画布上实现撤消/重做

Javascript Fabricjs-在画布上实现撤消/重做,javascript,fabricjs,implementation,undo-redo,Javascript,Fabricjs,Implementation,Undo Redo,我是vue和fabricjs的新手,目前正在开发一个处理大量数据的复杂应用程序。 我有多张不同对象的画布,这些画布可能会根据用户交互而改变。 所有更改/对象都存储在对象数组中 演示: 我正在尝试在我的应用程序中实现撤消/重做。 我很难理解下面答案(链接)中的fabricjs撤消/重做方法 从上面的答案(链接)中,我认为它一次处理单个画布,其中包含多个对象,但在我的情况下,我的画布对象存储在一个对象数组中,并将根据用户交互(如添加颜色、添加文本、,添加一次将在所有画布上执行的rect和撤消应反

我是vue和fabricjs的新手,目前正在开发一个处理大量数据的复杂应用程序。 我有多张不同对象的画布,这些画布可能会根据用户交互而改变。 所有更改/对象都存储在对象数组中

演示:

我正在尝试在我的应用程序中实现撤消/重做。 我很难理解下面答案(链接)中的fabricjs撤消/重做方法

从上面的答案(链接)中,我认为它一次处理单个画布,其中包含多个对象,但在我的情况下,我的画布对象存储在一个对象数组中,并将根据用户交互(如添加颜色、添加文本、,添加一次将在所有画布上执行的rect和撤消应反映在所有画布上。简而言之,我有多张画布,里面有相同的对象)

我尝试了下面的非结构方法,其中undo,redo作用于历史索引,该索引将根据undo(递增指针)、redo(递减指针)概念进行更改

基于这种方法,我称之为变异,其中

apply(state,actiontype){
if(actiontype==undo){
           remove the respective objects
           pop(data) //pops the data out of other places in the application not the history
           historyindex++;
}else if(actiontype==redo){
           get the data from history
           unshift(data);
           historyindex–-;
}
}
我觉得这不是执行撤消/重做操作的最有效方式,因为它包括克隆对象,甚至还必须处理大量数据集。这可能会导致应用程序冻结。任何建议都会很有帮助

代码:(多张画布)


已编辑:我已使用撤消/重做功能更新了示例,使用了结构历史库,仍在尝试找到更好的方法。

我认为您需要使用命令模式来完成此操作。它将比使用所有json数据更有效。为此,您需要实施下一种方法:

  • 创建一个用于存储历史的类。可能是这样的
  • 用于删除对象

    class AddCommand {
      constructor(receiver, controller) {
        this.receiver = receiver;
        this.controller = controller;
      }
      execute() {
        this.controller.addObject(this.receiver);
      }
      undo() {
        this.controller.removeObject(this.receiver);
      }
    }
    
    // When you will add object on your canvas invoke also this.history.add(new AddCommand(object, controller))
    
    class RemoveCommand {
      constructor(receiver, controller) {
        this.receiver = receiver;
        this.controller = controller;
      }
      execute() {
        this.controller.removeObject(this.receiver);
      }
      undo() {
        this.controller.addObject(this.receiver);
      }
    }
    
    fabric.js对每个对象都有saveState方法。您可以将其用于实现转换命令,当您在画布上修改fabric对象时,该命令将添加到历史对象JECCT中

    class TransformCommand {
      constructor(receiver, options = {}) {
        this.receiver = receiver;
        this._initStateProperties(options);
    
        this.state = {};
        this.prevState = {};
    
        this._saveState();
        this._savePrevState();
      }
      execute() {
        this._restoreState();
        this.receiver.setCoords();
      }
      undo() {
        this._restorePrevState();
        this.receiver.setCoords();
      }
      // private
      _initStateProperties(options) {
        this.stateProperties = this.receiver.stateProperties;
        if (options.stateProperties && options.stateProperties.length) {
          this.stateProperties.push(...options.stateProperties);
        }
      }
      _restoreState() {
        this._restore(this.state);
      }
      _restorePrevState() {
        this._restore(this.prevState);
      }
      _restore(state) {
        this.stateProperties.forEach((prop) => {
          this.receiver.set(prop, state[prop]);
        });
      }
      _saveState() {
        this.stateProperties.forEach((prop) => {
          this.state[prop] = this.receiver.get(prop);
        });
      }
      _savePrevState() {
        if (this.receiver._stateProperties) {
          this.stateProperties.forEach((prop) => {
            this.prevState[prop] = this.receiver._stateProperties[prop];
          });
        }
      }
    }
    

    现在,您可以在历史记录中添加命令并执行或撤消它们。

    请不要多次发布同一问题。
    class TransformCommand {
      constructor(receiver, options = {}) {
        this.receiver = receiver;
        this._initStateProperties(options);
    
        this.state = {};
        this.prevState = {};
    
        this._saveState();
        this._savePrevState();
      }
      execute() {
        this._restoreState();
        this.receiver.setCoords();
      }
      undo() {
        this._restorePrevState();
        this.receiver.setCoords();
      }
      // private
      _initStateProperties(options) {
        this.stateProperties = this.receiver.stateProperties;
        if (options.stateProperties && options.stateProperties.length) {
          this.stateProperties.push(...options.stateProperties);
        }
      }
      _restoreState() {
        this._restore(this.state);
      }
      _restorePrevState() {
        this._restore(this.prevState);
      }
      _restore(state) {
        this.stateProperties.forEach((prop) => {
          this.receiver.set(prop, state[prop]);
        });
      }
      _saveState() {
        this.stateProperties.forEach((prop) => {
          this.state[prop] = this.receiver.get(prop);
        });
      }
      _savePrevState() {
        if (this.receiver._stateProperties) {
          this.stateProperties.forEach((prop) => {
            this.prevState[prop] = this.receiver._stateProperties[prop];
          });
        }
      }
    }