【问题标题】:Fabricjs - Implementing undo/redo on canvasesFabricjs - 在画布上实现撤消/重做
【发布时间】:2020-08-11 01:22:14
【问题描述】:

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

演示:https://prkos.csb.app/

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

Undo-Redo feature in Fabric.js

我从上面的答案(链接)中想到的是,它一次处理单个画布,其中包含多个对象,但在我的情况下,我的画布对象存储在一个对象数组中,并将相应地修改基于用户交互,例如添加颜色、添加文本、添加矩形,这将一次在所有画布上执行,撤消应该反映在所有画布上。简而言之,我有多个画布,其中包含相同的对象)。

我尝试了以下非结构方法,其中撤消、重做对历史索引起作用,该索引将根据撤消(增量指针)、重做(减量指针)概念进行更改。

基于这种方法,我称之为突变

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

我觉得这不是执行撤消/重做操作的最有效方式,因为它包括克隆对象,甚至它必须处理大量数据。这可能会导致应用程序冻结。任何建议都会很有帮助。

代码:(多个画布)https://codesandbox.io/s/test-project-prkos?file=/src/components/App.vue

已编辑:我已使用撤消/重做功能更新了示例,使用了 fabric-history 库,仍在尝试寻找更好的方法。

【问题讨论】:

标签: javascript fabricjs implementation undo-redo


【解决方案1】:

我认为您需要为此使用命令模式。这将比使用所有 json 数据更有效。为此,您需要实现下一个方法:

  1. 创建一个用于存储历史的类。它可能看起来像这样
class CommandHistory {
  commands = [];
  index = 0;
  getIndex() {
    return this.index;
  }
  back() {
    if (this.index > 0) {
      let command = this.commands[--this.index];
      command.undo();
    }
    return this;
  }
  forward() {
    if (this.index < this.commands.length) {
      let command = this.commands[this.index++];
      command.execute();
    }
    return this;
  }
  add(command) {
    if (this.commands.length) {
      this.commands.splice(this.index, this.commands.length - this.index);
    }
    this.commands.push(command);
    this.index++;
    return this;
  }
  clear() {
    this.commands.length = 0;
    this.index = 0;
    return this;
  }
}

// use when you init your Canvas, like this.history = new CommandHistory();
  1. 那么您必须为您的命令实现命令类。

用于添加对象

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 对每个对象 http://fabricjs.com/docs/fabric.Object.html#saveState 都有 saveState 方法。您可以使用它来实现变换命令,当您在画布上修改织物对象时,该命令将添加到历史对象中。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-18
    • 2016-02-18
    • 1970-01-01
    • 2017-11-29
    • 2016-05-15
    • 2011-03-10
    相关资源
    最近更新 更多