我想实现Undo和Redo功能,不仅适用于客户端,也适用于服务器端。对于insatnce我有一个div包含图像,我可以旋转调整大小并重写它,图像生成的所有基本操作。并且所有操作都会更新数据库和图像。您可以说我的图像正在重新生成,并且每次操作后都会更新数据库。
现在我需要实现撤消和重做功能。我也做了一些研究。我需要的是如何实现所需任务的最佳方法。我正在考虑维护每个动作“日志类型的东西”或用数据库或javascript数组处理它,包括HTML或其他什么?
实现目标的最佳方法是什么。
谢谢,
答案 0 :(得分:23)
在基础层面,你需要两件事:
操作堆栈(数组),用于跟踪已执行的操作。当用户执行操作时,您将创建一个描述操作的对象并将其添加到阵列中。当用户点击撤消时,您可以从阵列中删除最后一项。
每种操作类型都需要“保存”方法和“撤消”方法。这可能会变得棘手,因为一些'撤消'方法类似于他们的'保存'方法(即撤消水平翻转你只是做另一次翻转),而其他人没有这种对称性(即撤消你需要的裁剪)存储作物发生前的图像数据。
如果您想要'重做'功能,那么您需要第二个操作堆栈。每次撤消操作时,都会将其添加到重做堆栈的末尾。如果用户点击“重做”,则再次将其移回操作堆栈。
查看Command模式(http://en.wikipedia.org/wiki/Command_pattern)可能会有所帮助,因为这通常用于实现撤消。
答案 1 :(得分:5)
我的javascript undo manager使用命令模式。基本上,对于每个操作,您还可以实现撤消操作和重做操作。您可以在服务器端构建相同的功能。
https://github.com/ArthurClemens/Javascript-Undo-Manager
这是命令模式的清晰代码示例: https://github.com/shichuan/javascript-patterns/blob/master/design-patterns/command.html
答案 2 :(得分:0)
好吧,我在一个项目上从事此工作,我想可以在这里显示代码了,我的情况可能与urs不完全相同,但是类似,所以我的情况是我存储了环境的状态,我想存储更改在这种环境下,但我决定不使用每一个不太聪明的更改来存储整个数组,所以我想使用JSON-Patch
这是一个很酷的协议来监视json数组中的更改,并且我可以存储这些补丁,因为它们比我的大型阵列小。
const jiff = require('jiff')
/**
* @description function chacks the difference between two states and returns the differece to be stored
* @param oldState @type Array
* @param newState @type Array
*/
///note when changing from a to b your looking for what would make b a not a b
const createAnOpp = (newState, oldState) => {
// console.log("\n newState",newState)
// console.log("\n oldState",oldState)
// console.log("\n new opp", jiff.diff( JSON.parse(JSON.stringify(newState)),JSON.parse(JSON.stringify(oldState))))
return jiff.diff( JSON.parse(JSON.stringify(newState)),JSON.parse(JSON.stringify(oldState)) )
};
/**
* @description takes an operation and applies the patch operation to data passed on by reference
* @param opperation @type reference
* @param data @type reference
*/
const perfomOpp =(opperation,data )=>{
return jiff.patch(opperation, data);
}
/**
* @description applies the do redo or undo feature based on the command sent to it
* @param code @type number 1 = redo 0 = undo
* @param data @type Array
* @param opperation
* @returns an object which is the state and the redo opp { newOpp,latestState}
*/
const performCall = (code,data,operation)=>{
switch(code){
case(0): ////undo
{
//patches on the list are stored to undo(go back to previous state)
const latestState = perfomOpp(operation,data)
// console.log("\n latestState",latestState)
// console.log("\n oldState",data)
return {
latestState ,
newOpp:createAnOpp(latestState,data)
}
break
}
case(1): ////redo
{
//patches on the list are stored to undo(go back to previous state)
const latestState = perfomOpp(operation,data)
// console.log('\n neww opp stage 1==>',createAnOpp(data,latestState))
return {
latestState ,
newOpp:createAnOpp(latestState,data)
}
break
}
}
}
///init state
var a = [
{ name: 'a' },
{ name: 'b' },
{ name: 'c' },
]
console.log("\n current Array ", a)
var opps = []
var pointerToOps = 0
var b = a.slice();
b.push({ name: 'd' });
// console.log("\n b==>",b)
console.log("\n current Array ", b)
// Generate diff (ie JSON Patch) from a to b
var patch = createAnOpp(b, a);
opps.push(patch)//store the diff when its been changed
pointerToOps = opps.length - 1
// console.log("\n opps1==>",JSON.stringify(opps))
//update the pointer
var c = b.slice();
c.push({ name: 'e' });
console.log("\n current Array ", c)
// console.log("\n c==>",c)
// Generate diff (ie JSON Patch) from a to b
var patch = createAnOpp(c, b);
opps.push(patch)//store the diff when its been changed
pointerToOps = opps.length - 1
console.log("\n opp ", opps)
//update the pointer
//now ive applied change and what not time to undo
const newData = performCall(0,c,opps[pointerToOps])
// //now i have to go take a step back with the pointer
opps[pointerToOps] = newData.newOpp//replacing undo opp with redo opp
pointerToOps = --pointerToOps
// console.log("\n opps3==>",JSON.stringify(opps))
console.log("\n current Array ", newData.latestState)
const newData2 = performCall(0,newData.latestState,opps[pointerToOps])
//now i have to go take a step back with the pointer
console.log("\n current Array ", newData2.latestState)
opps[pointerToOps] = newData2.newOpp//replacing undo opp with redo opp
pointerToOps = --pointerToOps
pointerToOps = ++pointerToOps
const newData3 = performCall(1,newData2.latestState,opps[pointerToOps])
//now i have to go take a step back with the pointer
opps[pointerToOps] = newData3.newOpp//replacing undo opp with redo opp
console.log("\n current Array ", newData3.latestState)
pointerToOps = ++pointerToOps
const newData4 = performCall(1,newData3.latestState,opps[pointerToOps])
//now i have to go take a step back with the pointer
opps[pointerToOps] = newData4.newOpp//replacing undo opp with redo opp
console.log("\n current Array ", newData4.latestState)
console.log("\n opp ", opps)