我想在我的脚本中添加一个“撤销/重做”功能。我查看了一些建议,大多数建议使用“命令模式”。
该功能必须适用于一个页面 - 在页面重新加载后,该功能必须能够“重做/撤销”最后的操作。
我不知道命令模式是如何工作的,我考虑创建一个对象,来存储函数的名称、旧值和新值,但我不确定这是否是有效的方法。
也许有人可以给我一个小的示例,展示一个“撤销/重做”函数的代码应该是什么样的。
我想在我的脚本中添加一个“撤销/重做”功能。我查看了一些建议,大多数建议使用“命令模式”。
该功能必须适用于一个页面 - 在页面重新加载后,该功能必须能够“重做/撤销”最后的操作。
我不知道命令模式是如何工作的,我考虑创建一个对象,来存储函数的名称、旧值和新值,但我不确定这是否是有效的方法。
也许有人可以给我一个小的示例,展示一个“撤销/重做”函数的代码应该是什么样的。
有两种常见实现撤销/重做的方法:
此方法易于实现,但效率低下,因为您需要存储相似副本的整个状态。
这种方式更难实现,因为对于您应用程序中的每个可撤销操作都必须明确编写其反向操作,但它更节省内存,因为您只存储影响状态的操作。
现在让我们详细介绍这两种模式:
在应用程序执行操作之前,您会对当前状态进行快照,并将其保存到数组中。该快照称为备忘录。
如果用户想要撤消,则只需pop
上一个备忘录并应用它即可。程序将返回到在应用最后一个动作之前的状态。
这种模式占用内存较多;每个备忘录相对较大,因为它捕获了整个当前状态。
但它也是最容易实现的,因为您不需要像命令模式(见下文)中那样显式编写所有情况及其反向操作。
const mementos = []
const input = document.querySelector('input')
function saveMemento() {
mementos.push(input.value)
}
function undo() {
const lastMemento = mementos.pop()
input.value = lastMemento ? lastMemento : input.value
}
<h4> Type some characters and hit Undo </h4>
<input onkeydown="saveMemento()" value="Hello World"/>
<button onclick="undo()">Undo</button>
对于用户采取的每个操作,您都需要保存相应的逆操作/命令。例如,每次在文本框中添加字符时,都要保存逆函数;即删除该位置处的字符。
如果用户希望撤消,则弹出最后一个逆操作/命令并应用它。
const commands = []
const input = document.querySelector('input')
function saveCommand(e) {
commands.push({
// the action is also saved for implementing redo, which
// is not implemented in this example.
action: { type: 'add', key: e.key, index: input.selectionStart },
inverse: { type: 'remove', index: input.selectionStart }
})
}
function undo() {
let value = input.value.split('')
const lastCommand = commands.pop()
if (!lastCommand) return
switch (lastCommand.inverse.type) {
case 'remove':
value.splice(lastCommand.inverse.index, 1)
break;
}
input.value = value.join('')
}
<h4> Type some characters and hit Undo </h4>
<input onkeydown="saveCommand(event)" value="Hello World"/>
<button onclick="undo()">Undo</button>
我写的这些片段仅在添加字符时才有效,然后撤消以返回到添加字符之前的状态,因此它们是对您应该如何实现此操作的过度简化。
尽管如此,我认为它们演示了两种模式的核心概念。
顺便说一句,我在我的项目中使用UndoManager作为我的命令堆栈。