Fabricjs - 在画布上实现undoredo的功能

问题描述 投票:0回答:1

我是一个vue和fabricjs的新手,目前正在处理一个复杂的应用程序,其中处理大量的数据.我有多个canvases与不同的对象,这可能会改变根据用户的互动.所有的变化objects被存储在对象数组中。

演示。https:/prkos.csb.app.enter image description here

我正试图在我的应用程序中实现undoredo.我很难理解下面答案中的fabricjs undoredo方法(链接)。

Fabric.js中的撤消-重做功能

从上面的答案(链接)中,我认为它处理的是一次有多个对象的单个画布,但在我的情况下,我的画布对象存储在一个对象数组中,它将根据用户的交互进行相应的修改,如添加颜色、添加文本、添加矩形,这将在所有的画布上同时进行,撤消应该反映在所有的画布上。简而言之,我有多个canvases,里面有相同的对象)。)

我尝试了下面的非fabric方法,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–-;
}
}

我觉得这不是执行undoredo操作的最有效的方式,因为它包括克隆对象,甚至它必须处理巨大的数据集。这可能会导致应用程序的冻结。任何建议都将是非常有用的。

代码:(多个Canvases) https:/codesandbox.iostest-project-prkos?file=srccomponentsApp.vue。

编辑了。我已经用undoredo功能更新了这个例子,使用了fabric-history库,还在努力寻找更好的方法。

javascript fabricjs implementation undo-redo
1个回答
0
投票

我认为你需要使用命令模式来处理这个问题。它将比使用所有的json数据更有效。为此,你需要实现下一个方法。

  1. 创建一个存储History的类 它可能是这样的
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中的每个对象都有saveState方法。http:/fabricjs.comdocsfabric.Object.html#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];
      });
    }
  }
}

现在你可以将你的命令添加到历史记录中,并执行或撤销它们。

© www.soinside.com 2019 - 2024. All rights reserved.