实现撤销和重做功能 javascript 和 php

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8837387/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 04:43:50  来源:igfitidea点击:

Implementing Undo and Redo functionality javascript and php

phpjavascriptundoredo

提问by hashmi

I want to implement Undo and Redo functionality not only for client side but for server side as well. For insatnce i have a div containing image and i can rotate resize and rewrite it , All the basic operations for image generation. And all of the operations update databse and image. you can say my image is being regenerated and database is updated after every action.

我想不仅为客户端而且为服务器端实现撤消和重做功能。对于insatnce,我有一个包含图像的div,我可以旋转调整大小并重写它,图像生成的所有基本操作。并且所有操作都会更新数据库和图像。您可以说我的图像正在重新生成,并且在每次操作后都会更新数据库。

Now i need to implement Undo and Redo functionality. I have done some research as well. What i need is the best approach how to implement the required task. I was thinking to maintain each action "log type thing" or handle it with database or with javascript arrays including HTML or what else??

现在我需要实现撤消和重做功能。我也做了一些研究。我需要的是如何实现所需任务的最佳方法。我正在考虑维护每个操作“日志类型的东西”或使用数据库或包含 HTML 或其他什么的 javascript 数组来处理它?

what is the best approach to achieve my goal.

实现我的目标的最佳方法是什么。

Thanks,

谢谢,

回答by Graham

At a basic level, you need two things:

在基本层面上,您需要做两件事:

  • an operation stack (array) which keeps track of the operations that have been performed. When the user performs an operation, you create an object that describes the operation and add it to the array. When the user hits undo, you can remove the last item from the array.

  • each operation type needs a 'save' method and an 'undo' method. This can get tricky as some 'undo' methods are similar to their 'save' method (i.e. to undo a horizontal flip you just do another flip), whereas others do not have such symmetry (i.e. to undo a crop you'd have to store the image data as it was before the crop occurred).

  • 一个操作堆栈(数组),用于跟踪已执行的操作。当用户执行操作时,您创建一个描述该操作的对象并将其添加到数组中。当用户点击撤销时,您可以从数组中删除最后一项。

  • 每个操作类型都需要一个“save”方法和一个“undo”方法。这可能会变得棘手,因为一些“撤消”方法类似于他们的“保存”方法(即,要撤消水平翻转,您只需再进行一次翻转),而其他方法则没有这种对称性(即,要撤消裁剪,您必须将图像数据存储为裁剪发生前的状态)。

If you want 'redo' functionality, then you'd need a second operation stack. Each time an operation was undone, you'd add it to the end of the redo stack. If the user hits 'Redo', then you move it back to the operation stack again.

如果您想要“重做”功能,那么您需要第二个操作堆栈。每次撤消操作时,您都会将其添加到重做堆栈的末尾。如果用户点击“重做”,则您将其再次移回操作堆栈。

It may help to look into the Command pattern (http://en.wikipedia.org/wiki/Command_pattern), as this is often used to implement Undo.

查看命令模式 (http://en.wikipedia.org/wiki/Command_pattern) 可能会有所帮助,因为这通常用于实现撤消。

回答by Arthur Clemens

My javascript undo manager uses the command pattern. Basically, for each action you also implement an undo action and a redo action. You could build the same functionality serverside.

我的 javascript 撤销管理器使用命令模式。基本上,对于每个操作,您还实现了一个撤消操作和一个重做操作。您可以在服务器端构建相同的功能。

https://github.com/ArthurClemens/Javascript-Undo-Manager

https://github.com/ArthurClemens/Javascript-Undo-Manager

And this is a clear code example of the command pattern: https://github.com/shichuan/javascript-patterns/blob/master/design-patterns/command.html

这是命令模式的清晰代码示例:https: //github.com/shichuan/javascript-patterns/blob/master/design-patterns/command.html

回答by user4920718

Well I worked on this for a project I guess it's ok to show the code here, my scenario may not be exactly like urs but similar, so my case was I stored the state of an environment, i wanted to store the changes to this environment but i decided not to use store the whole array for every change that would not be smart i guess so i used something called JSON-Patchits a cool protocol to monitor the changes in a json array and i can store those patches because they are smaller than my massive array.

嗯,我为一个项目工作过,我想在这里显示代码是可以的,我的场景可能与 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)