【问题标题】:Implementing Undo and Redo functionality javascript and php实现撤消和重做功能 javascript 和 php
【发布时间】:2012-02-08 21:04:42
【问题描述】:

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

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

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

谢谢,

【问题讨论】:

  • 你能发布你目前拥有的东西吗?
  • 有一个众所周知的Command design pattern,非常适合undo-redo功能。

标签: php javascript undo redo


【解决方案1】:

好吧,我为一个项目工作,我想在这里显示代码是可以的,我的场景可能不完全像 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)

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

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

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

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

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

      【讨论】:

      • 撤消/重做也可以通过执行单个命令数组来实现,并使用索引/指针指向可以向上或向下移动堆栈的最后一个活动操作......但通常以以太网方式工作,只需节省双倍内存
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-01
      • 2011-11-14
      • 2014-04-20
      • 2012-06-02
      • 2012-12-16
      • 1970-01-01
      相关资源
      最近更新 更多