我是一个vue和fabricjs的新手,目前正在处理一个复杂的应用程序,其中处理大量的数据.我有多个canvases与不同的对象,这可能会改变根据用户的互动.所有的变化objects被存储在对象数组中。
我正试图在我的应用程序中实现undoredo.我很难理解下面答案中的fabricjs undoredo方法(链接)。
从上面的答案(链接)中,我认为它处理的是一次有多个对象的单个画布,但在我的情况下,我的画布对象存储在一个对象数组中,它将根据用户的交互进行相应的修改,如添加颜色、添加文本、添加矩形,这将在所有的画布上同时进行,撤消应该反映在所有的画布上。简而言之,我有多个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库,还在努力寻找更好的方法。
我认为你需要使用命令模式来处理这个问题。它将比使用所有的json数据更有效。为此,你需要实现下一个方法。
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();
对于添加对象
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];
});
}
}
}
现在你可以将你的命令添加到历史记录中,并执行或撤销它们。