如何制作 undo/redo 函数
How to make a undo/redo function
我想在我的脚本中添加一个 undo/redo
函数。我环顾四周,看到了一些建议,其中大部分建议使用 command pattern
.
函数必须在一页上工作 - 重新加载页面后函数必须能够redo/undo
最后的事情。
我不知道命令模式是如何工作的,我想创建一个对象来存储函数的名称、旧值和新值 - 但我不确定这是否有效这样做与否。
也许有人可以给我一个小例子 undo/redo
函数的代码应该是什么样子。
有两种常见的实现方式 undo/redo:
- Memento Pattern,您可以在其中捕获整个 当前状态。它易于实现,但内存效率低下,因为您需要存储整个状态的相似副本。
- Command Pattern,您在其中捕获影响状态的 commands/actions逆作用)。更难实现,因为对于应用程序中的每个可撤消操作,您必须明确编码它的反向操作,但它的内存效率要高得多,因为您只存储影响状态的操作。
纪念品模式
在应用操作之前,您拍摄当前状态的快照并将其保存到数组中。该快照是 Memento.
如果用户想要撤消,您只需 pop
最后的纪念品并应用它。程序 return 恢复到应用最后一个操作之前的状态。
这个模式是内存密集型的;每个纪念品都比较大,因为它捕获了整个当前状态。
但它也是最容易实现的,因为您不需要在命令模式(见下文)中显式编码所有情况及其逆向操作。
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>
命令模式
对于用户执行的每个操作,您还会保存相应的 inverse action/command。例如,每次在文本框中添加字符时,都保存反函数;也就是删除那个位置的字符。
如果用户想要撤消,你弹出最后一个逆action/command并应用它。
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>
我编写的代码片段仅在添加字符时有效,然后点击撤消 return 到添加字符之前的状态,因此它们过于简化了您应该如何实现这一点。
尽管如此,我认为它们展示了两种模式的核心概念。
FWIW 我在我的项目中使用 UndoManager 作为命令堆栈。
我想在我的脚本中添加一个 undo/redo
函数。我环顾四周,看到了一些建议,其中大部分建议使用 command pattern
.
函数必须在一页上工作 - 重新加载页面后函数必须能够redo/undo
最后的事情。
我不知道命令模式是如何工作的,我想创建一个对象来存储函数的名称、旧值和新值 - 但我不确定这是否有效这样做与否。
也许有人可以给我一个小例子 undo/redo
函数的代码应该是什么样子。
有两种常见的实现方式 undo/redo:
- Memento Pattern,您可以在其中捕获整个 当前状态。它易于实现,但内存效率低下,因为您需要存储整个状态的相似副本。
- Command Pattern,您在其中捕获影响状态的 commands/actions逆作用)。更难实现,因为对于应用程序中的每个可撤消操作,您必须明确编码它的反向操作,但它的内存效率要高得多,因为您只存储影响状态的操作。
纪念品模式
在应用操作之前,您拍摄当前状态的快照并将其保存到数组中。该快照是 Memento.
如果用户想要撤消,您只需 pop
最后的纪念品并应用它。程序 return 恢复到应用最后一个操作之前的状态。
这个模式是内存密集型的;每个纪念品都比较大,因为它捕获了整个当前状态。
但它也是最容易实现的,因为您不需要在命令模式(见下文)中显式编码所有情况及其逆向操作。
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>
命令模式
对于用户执行的每个操作,您还会保存相应的 inverse action/command。例如,每次在文本框中添加字符时,都保存反函数;也就是删除那个位置的字符。
如果用户想要撤消,你弹出最后一个逆action/command并应用它。
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>
我编写的代码片段仅在添加字符时有效,然后点击撤消 return 到添加字符之前的状态,因此它们过于简化了您应该如何实现这一点。
尽管如此,我认为它们展示了两种模式的核心概念。
FWIW 我在我的项目中使用 UndoManager 作为命令堆栈。