如何制作撤消/重做功能

2024-01-08

我想添加一个undo/redo我的脚本中的函数。我环顾四周,看到了一些建议,其中大多数建议使用command pattern.

该函数必须在一页上运行 - 重新加载页面后,该函数必须能够redo/undo最后的事情。

我不知道命令模式是如何工作的,我想创建一个对象,来存储函数的名称、旧值和新值 - 但我不确定这是否是执行此操作的有效方法或不是。

也许有人可以给我一个小例子,如何编写undo/redo函数应该看起来。


有两种常见的实现撤消/重做的方法:

  • The 纪念品图案 https://en.wikipedia.org/wiki/Memento_pattern,您可以在其中捕获整体current state https://en.wikipedia.org/wiki/State_(computer_science).

它很容易实现,但内存效率低下,因为您需要存储整个状态的类似副本。

  • The 命令模式 https://en.wikipedia.org/wiki/Command_pattern,你捕获的地方命令/动作影响国家的;这当前行动这是逆动作.

这更难实现,因为对于应用程序中的每个可撤消操作,您必须显式编码其相反操作,但它的内存效率要高得多,因为您只存储影响状态的操作。

现在让我们详细说明这两种模式:

纪念品模式

在应用操作之前,您可以拍摄当前状态的快照并将其保存到数组中。那个快照是Memento.

如果用户想要撤消,您只需pop最后的纪念品并应用它。程序返回到应用最后一个操作之前的状态。

这种模式需要大量内存;每个纪念品都相对较大,因为它捕获了whole当前状态。

但这也是最容易实现的,因为您不需要在命令模式中显式编码所有情况及其相反的操作(见下文)。

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动作/命令。例如,每次在文本框中添加字符时,都会保存反函数;也就是删除该位置的字符。

如果用户想要撤消,您可以弹出最后一个相反的操作/命令并应用它。

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>

我编写的代码片段仅在添加字符时起作用,然后单击撤消以返回到添加字符之前的状态,因此它们对您应该如何实现这一点过于简单化。

尽管如此,我认为它们展示了两种模式的核心概念。

FWIW我正在使用撤消管理器 https://github.com/ArthurClemens/Javascript-Undo-Manager在我的项目中作为我的命令的堆栈。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何制作撤消/重做功能 的相关文章

  • 使用 .add() 选择多个 jQuery 对象

    是否 add http api jquery com add 方法允许一次选择多个对象而不是一次添加一个 one add two add three add four on click function 以下变量的设置方式相同 因为每个变量
  • 使用把手显示来自 parse.com 的 json 响应

    我想将 json 响应传递给车把 我已经查看了解析文档和 stackoverflow 问题 但我似乎无法弄清楚这一点 这是回应 results address 755 W Yale createdAt 2013 02 09T01 12 15
  • Firebase Auth - 最近登录多长时间

    我有一个个人资料选项卡 用户可以在其中按编辑并编辑他们的个人资料 我只想在必要时才需要他们的密码 所以想知道用户登录的时间是多少毫秒 这使得它不是最近登录 其中firebase会抛出错误 auth requires recent login
  • 不使用 PHP 提交联系表单

    我还是一名学生 今天我们的讲师告诉我们 无需使用 mailto 函数即可提交联系我们表单的唯一方法是使用 PHP 我发誓去年另一位讲师向我们展示了一种仅使用 javascript 的方法 是否可以使用基本表单和 javascript 提交反
  • 使用 Jquery 附加链接

    我正在尝试根据您所在的页面添加指向我的页面的链接 我使用 Squarespace 来构建这个网站 因此对我来说最简单的方法是使用 Javascript 或 Jquery 我认为我缺少的这个语法有问题 我已经尝试用 来打破引号 但这不起作用
  • asp.net 将值从 JS/jquery 传递到 C# 背后的代码

    我已经尝试了 所有 可能的方法 将 screen width vlaue 从 aspx 页面上的 JS 脚本发送到后面代码中的 c 虽然我可以看到 screen width 被正确分配 但它永远不会分配给我的隐藏字段价值
  • 无法在 IE 中的选择选项上使用 onmouseover 事件

    更新的代码 function getElements var x document getElementsByTagName option var el document getElementById selectDept el onmou
  • 所有事件的 HTML5 EventSource 监听器?

    我使用 EventSource 在 JavaScript 客户端应用程序中推送通知 我可以像这样附加事件监听器 source addEventListener my custom event type function e console
  • React Native TypeError:无法读取未定义的属性“createClient”

    我是 React 本机框架的新手 我使用 create react native app AwesomeProject 创建了应用程序 我想在我的项目中使用 BLE 因此我安装了 react native ble plx 模块 但是当我创建
  • 用于传输命名参数和正文的云端点资源属性不起作用

    我正在尝试通过gapi client rpc调用实现对谷歌云端点的调用 如文档中所述 和Google Cloud Endpoints 使用 JS 客户端进行调用 传递参数和 JSON 正文 https stackoverflow com q
  • 在 JQuery ui 自动完成中显示图像

    我有一个带有 JQuery ui 自动完成功能的脚本 可以完美运行 有一个显示用户名字和姓氏的搜索过程 但在我的数据库中 还有用户的图片 我想将其显示在带有名字和姓氏的建议中 数据库中pic包含图片url 剧本 function searc
  • Angularjs - 将 True/False 显示为 Yes/No

    有没有一种简单的方法可以将真 假值显示为是 否 我正在从数据库检索包含以下内容的 JSON 对象 对象 WithCertification true 这是 HTML 有认证 elem WithCertification 正在显示这个 认证真
  • 在移动网站中处理 iPhone 事件(如向左滑动)

    iPhone 浏览器是否有可以使用 Javascript 挂钩的特殊事件 例如 如果用户向左滑动 我想执行某个操作 如果有类似的活动 很高兴看到所有这些活动的参考 理想情况下 有一天所有触摸屏移动浏览器都会有一个标准 您可以访问多点触控事件
  • 我们如何使用 thymeleaf 绑定对象列表的列表

    我有一个表单 用户可以在其中添加任意数量的内容表对象这也可以包含他想要的列对象 就像在 SQL 中构建表一样 我尝试了下面的代码 但没有任何效果 并且当我尝试绑定两个列表时 表单不再出现 控制器 ModelAttribute page pu
  • iPhone 点击时使 div 变暗

    当您的 div 附加了点击处理程序时 当点击该 div 时 iPhone 会使该 div 变暗 作为点击指示器 示例 在移动 Safari 上查看http jsbin com awejo3 4 http jsbin com awejo3 4
  • 如何使用 Jquery .animate() 函数创建连续滚动内容? [复制]

    这个问题在这里已经有答案了 可能的重复 在jquery中实现圆形滚动条 https stackoverflow com questions 812049 implementing circular scroller in jquery 我想
  • 什么是 TinyMCE jQuery 包?

    我被要求在项目中使用 TinyMCE 编辑器 在下载页面上 有一个主包 然后是一个 jQuery 包 This package contains special jQuery build of TinyMCE and a jQuery in
  • YouTube iFrame Player API 无法在 DOMWindow 上执行 postMessage

    我正在尝试使用以下命令将 youtube 加载到我的网页中YouTube iFrame Player API 并在加载时出现以下错误 Failed to execute postMessage on DOMWindow The target
  • 这个 JQuery 指令做什么 $(function(){...}) [重复]

    这个问题在这里已经有答案了 我最近一直在研究JQuery 尽管我知道一些东西 但书上有这样一句话我根本无法理解 function current entry 1 有谁知道这条线是如何工作的以及它的作用是什么 它类似于 JQuery 函数中的
  • 如何在 Twilio 可编程聊天中的单个通道上侦听消息

    Using twilio chat js https www npmjs com package twilio chat如何在单个频道上收听消息 我发现这个问题 https stackoverflow com questions 54687

随机推荐

  • 沿一个坐标轴的 3D 倾斜变换矩阵

    有没有一种方法可以计算沿一个坐标轴的倾斜变换矩阵 给定倾斜角度 如下 这应该在很大程度上适用于使用变换矩阵倾斜对象 特别是使用 glMultMatrix matrix matrix1 1 0 0 0 tan a 1 0 0 0 0 1 0
  • Cordova 视频编辑器插件 InvalidOutputFormatException

    我在用着科尔多瓦插件视频编辑器 https www npmjs com package cordova plugin video editor处理以前录制的视频 该插件开始转换 但由于某种原因失败 我得到的错误是这样的 net yprest
  • 该程序集是由比当前加载的运行时更新的运行时构建的,无法加载

    我收到此错误 无法加载文件或程序集 Twitterizer2 Asynchronous 或其中之一 它的依赖项 该程序集是由比 当前已加载运行时且无法加载 但我已经从我的引用中删除了 twitterizer2 Asynchronous 库
  • 如何管理服务器上的数据库连接?

    我的 Web 应用程序中的数据库连接存在严重问题 由于我对单例数据库类的整个应用程序使用单个数据库连接 因此如果我尝试并发数据库操作 两个用户 数据库将回滚事务 这是我使用的静态方法 所有线程 servlet 都调用静态 Database
  • 当单元格更改时,VBA 运行宏,但如果通过宏则不运行

    好吧 我不确定这是否容易实现 但我会尝试 如果单元格发生更改 我使用此子程序来执行一些宏 Private Sub Worksheet Calculate Dim target As Range Set target Range b4 If
  • 将蛇形字符串转换为标题大小写

    我有以下蛇形变量big animal我想将其转换为Big Animal 我的方法是str gt str replace w g g gt g 1 toUpperCase 但我不断得到bigAnimal 但我想保留空格并将第一个字母大写 另一
  • C - 未定义的引用 - 是否有使用 -lm 编译的替代方法?

    我有一个很像这个问题的问题here https stackoverflow com questions 5248919 c undefined reference to sqrt其中错误是 未定义对 sqrt 的引用 我了解如何在编译时使用
  • Python 连接列表中的数组

    我有一个相同大小的数组列表 列表 z 包含 gt gt gt z 0 Out 24 array 27 56272878 952 8099842 3378 58996244 4303 9692863 gt gt gt z 1 Out 25 a
  • 如何在 Google Chrome 中暂时禁用 websocket?

    我想暂时禁用 Google Chrome 中的 websocket 来调试 javascript 以确保它适用于任何不支持 websocket 的浏览器 首先 我想说有更好的机制可以在无 websocket 环境中测试 JavaScript
  • 限制对 ASP.Net MVC 站点的匿名访问的问题

    每当我限制 MVC 站点中的匿名访问时 我都会收到 404 错误 应用程序中的服务器错误 无法找到该资源 描述 HTTP 404 您正在查找的资源 或其依赖项之一 可能已被删除 名称已更改或暂时不可用 请检查以下 URL 并确保其拼写正确
  • 跨不同程序开发的库的 Subversion 存储库布局

    我负责几个 相当小的 程序 它们通过不同的库共享大量代码 我想知道开发不同程序 和库 的最佳存储库布局是什么 并使库在所有程序之间保持同步 为了便于讨论 我们假设有两个带有两个库的程序 Program1 Library1 Library2
  • 检查文件是否存在的最快方法[重复]

    这个问题在这里已经有答案了 我使用的方法只是尝试 fopen 要检查的文件 does file exist char fname 999 whatever constructed during execution FILE fp NULL
  • sql.Identifier 不带引号

    我在网上搜索 sql Identifier without quoting 并且这个帖子 https stackoverflow com questions 43877210 how to remove the quotes from a
  • R 闪亮登录黑客

    在尝试了 RStudio Shiny Pro Server 的评估后 我对登录 身份验证机制不太感兴趣 因为它们不是管理客户端访问闪亮应用程序的用户帐户的简单机制 因此 我试图在 Shiny 中创建自己的登录机制 除了在shinydashb
  • .NET 客户端编程中出现异常“AMQP 操作被中断”(代码=406)

    我有一个 2 8 2 RabbitMQ 服务器和一个 2 8 2 客户端 dll 有声明队列并获取消息的代码 它工作正常 所有参数都正确 IModel channel null ConnectionFactory factory new C
  • Windows 工作流程 4.0 InstancePersistenceCommand 错误

    我有一个 wcf 工作流程 其中有三个按顺序排列的接收活动 IE 1 接收申请 2 经理1批准 3 最终经理批准 我的问题是 我能够使用客户端应用程序中的工作流程并调用第一个 接收应用程序活动 但是当我尝试调用第二个 manager1 批准
  • Android 中的轮播动画

    请参阅下面的屏幕 我尝试使用封面流来实现此动画 就像我们作为轮播动画一样 但我没有得到与屏幕中所示相同的效果 如果有人做过这种类型的动画 请将代码片段发送给我 我前段时间在我的应用程序中遇到了类似的问题 虽然我没有处理过您在图像右下角显示的
  • 在 R 中的同一张图上绘制多列[重复]

    这个问题在这里已经有答案了 我有以下数据框 A B C D Xax 0 451 0 333 0 034 0 173 0 22 0 491 0 270 0 033 0 207 0 34 0 389 0 249 0 084 0 271 0 54
  • RxJava2 + Room:clearAllTables() 调用后数据未插入数据库

    成功后在我的Android应用程序中login我将会话信息保存在 Room 中 然后从 BE 检索用户信息并保存它 一切正常 我可以看到数据库表中保存的信息 When 用户注销从应用程序中 所有表都通过 appDatabase clearA
  • 如何制作撤消/重做功能

    我想添加一个undo redo我的脚本中的函数 我环顾四周 看到了一些建议 其中大多数建议使用command pattern 该函数必须在一页上运行 重新加载页面后 该函数必须能够redo undo最后的事情 我不知道命令模式是如何工作的