在web应用中,用户在进行一些富交互行为的操作时难免会出现误操作,比如在富文本编辑器设置错了字体颜色就需要撤回,做H5活动页面的时候不小心删了一个图片也需要撤回,更比如在线设计原型图应用的时候不小心删了一个页面等,总之在交互场景非常复杂的情况下,用户操作失误的可能性非常大,这时候‘撤销’和‘前进’这两个操作就很有必要了,而且用户体验也很好

思路

不管是任何场景下的web应用,用户的每一次操作我们都可以看成是对某个组件或某个对象的状态和属性进行改变,一旦连续的动作操作完成正准备进行下一个动作之前,此刻的状态就是一个全新的状态

A —— B —— C
用户未操作的时候全局状态是A
用户操作某个组件使其移动到位置X,松开鼠标之后全局状态是B
用户操作另一个组件使其删除,完成后全局状态是C

所以,撤销的操作就是在用户操作状态到C的时候让全局的状态回到B,回到上一次操作完的时候。
那么就需要可以存放这种大量状态的列表或索引来记录每一次操作的动作

但如果我用某一个数组变量来存储如此庞大的数据是不是略显不妥?数据量越大内存应该会爆吧?所以这里我推荐大家使用IndexedDB
下面是利用Angular、Rxjs和IndexedDB封装好的一个服务类

import { Inject } from "@angular/core";
import { IndexedDBAngular } from "indexeddb-angular";
import { Subject, Observer, Observable } from "rxjs";export interface IDBData {widgetList: string
}// 前进和后退的服务
@Inject({providedIn: 'root'
})
export class PanelExtendMoveBackService {/*** 发射DB集合存储的数据,可订阅*/public launchDBDataValue$: Subject<IDBData> = new Subject<IDBData>()/*** 创建一个叫panelDataDB的本地数据库,版本号为1*/public db = new IndexedDBAngular('panelDataDB', 1)/*** 记录前进和后退的存储集合项的下标key* 默认为0*/public dbCurrentIndex: number = 0/*** 自增的DBkey*/public dbKey: number = -1// 是否允许前进public get isMove() : boolean {return this.dbCurrentIndex < this.dbKey}// 是否允许后退public get isBack() : boolean {return this.dbCurrentIndex > 0}constructor() {}/*** 创建DB集合*/public createCollections(): Observable<boolean> {const _sub: Subject<boolean> = new Subject<boolean>()this.dbKey = -1this.db.createStore(1, (db: any) => {db.currentTarget.result.createObjectStore('panelItem')}).then(()=>{this.dbClear()_sub.next(true)})return _sub.asObservable()}/*** 往集合里添加数据* 同时把新添加的key赋值给dbCurrentIndex,*/public dbAdd(): void {this.handleDbCurrentRefreshDB();this.dbKey += 1;// 此处存储你要保存的数据const _widget_list = []this.db.add('panelItem', { widgetList: JSON.stringify(_widget_list) }, this.dbKey).then(_e => {if ((<Object>_e).hasOwnProperty('key')) {this.dbCurrentIndex = _e.key};},() => {this.dbKey -= 1throw new Error('添加panelItem集合失败')})}/*** 在执行添加数据集操作的时候判断dbCurrentIndex当前指引的下标是否低于dbKey* 如果是说明执行了后退操作之后后续动作执行了dbAdd的操作,则清空dbCurrentIndex索引之后的数据重新添加*/public handleDbCurrentRefreshDB(): void {if (this.dbCurrentIndex < this.dbKey) {for (let i = this.dbCurrentIndex + 1; i <= this.dbKey; i++) {this.db.delete('panelItem', i).then(() => {})}this.dbKey = this.dbCurrentIndex}}/*** 执行后退操作发射DB数据集*/public acquireBackDBData(): void {if( this.isBack ) {this.dbCurrentIndex -= 1this.db.getByKey('panelItem', this.dbCurrentIndex).then(res=>{this.launchDBDataValue$.next(res)},()=>{ })}}/*** 执行前进操作发射DB数据集*/public acquireMoveDBData(): void {if( this.isMove ) {this.dbCurrentIndex += 1this.db.getByKey('panelItem', this.dbCurrentIndex).then(res => {this.launchDBDataValue$.next(res)}, () => { })}}/*** 清除DB集合panelItem*/public dbClear(): void {this.db.clear('panelItem').then(_e => {})}
}

这里我偷懒了一下,直接采用自增的id作为key了,也方便查找
每一次操作所存储的数据如下

最后可以看一下我实现好了的撤销和前进操作的场景

富交互Web应用中的撤销和前进相关推荐

  1. 图片 富文本 粘贴_用C++实现富文本控件(中): 撤销

    用C++实现富文本控件: 撤销 本节是关于撤销重做相关实现. 项目地址: Github-RichED 本文备份地址: github 撤销重做 这就被称为UNDO/REDO之类的, 简直是'增量'的代表 ...

  2. java sessionstate_在Java Web开发中自定义Session

    Session在存储安全性要求较高的会话信息方面是必不可少的,对于分布式Web应用自定义Session支持独立的状态服务器或集群是必须的.本文就来教大家如何在Java Web开发中自定义Session ...

  3. Web 开发中很实用的10个效果【附源码下载】

    在工作中,我们可能会用到各种交互效果.而这些效果在平常翻看文章的时候碰到很多,但是一时半会又想不起来在哪,所以养成知识整理的习惯是很有必要的.这篇文章给大家推荐10个在 Web 开发中很有用的效果,记 ...

  4. 第十一章:WEB浏览器中的javascript

    客户端javascript涵盖在本系列的第二部分第10章,主要讲解javascript是如何在web浏览器中实现的,这些章节介绍了大量的脚本宿主对象,这些对象可以表示浏览器窗口.文档树的内容.这些章节 ...

  5. 用WSE在Web服务中验证用户身份

    一.Web服务安全与WS-Security 毫无疑问,SOAP和XML Web服务在交互操作和标准上已经完全改变了电子商务领域的格局. 然而直到最近,在Web服务技术领域仍然存在着一些缺陷,那就是处理 ...

  6. Java Web - 服务器中的过滤器和监听器

    一 过滤器 每个servlet都有自己需要处理的资源,这么多的资源我们有时候我们希望统一的进行管理,比如对响应的图片进行加水印处理,对提交和响应的数据的编码进行统一,或者是对提交的数据进行过滤. 解决 ...

  7. SOA 设计原则和 Web 服务中的数据传输

    SOA 被翻译为面向服务架构,它应该是创建从自治服务生成系统的一个架构方法,它的目的是更方便地进行集成. SOA是Web 服务的一个自然延伸,因为Web 服务只是解决了异构系统之间的互操作,并没有降低 ...

  8. Thinkphp下嵌套UEditor富文本WEB编辑器

    UEditor是由百度web前端研发部开发所见即所得富文本web编辑器,具有轻量,可定制,注重用户体验等特点,开源基于MIT协议,允许自由使用和修改代码... 本文实际操作于ThinkPHP框架下,现 ...

  9. Web站点下的Web Service读取此Web站点中的Session值

    在用Ajax:AutoCompleteExtender控件时,碰到自动完成的数据没办法按当前登录用户过滤权限的问题,主要是因为AutoComplete.asmx中自动完成的Method参数是固定的,不 ...

最新文章

  1. wpf 绘制rectangle 代码
  2. ASP.NET3.5问题集
  3. 的环境下 qt 运行在_Ubuntu16.04环境下运行vins mono(环境配置及编译)之ROS kinetic的安装...
  4. 【C++】max_element() 和 min_element()
  5. 推荐两个不错的前端资源的网站,有好的请继续添加,谢谢!
  6. 穷爸爸与富爸爸,背后思维的差异
  7. 手写Promise 封装Promise resolve reject then catch Promise.resolve Promise.reject
  8. php中理解print EOT分界符和echo EOT的用法区别
  9. SpringBoot整合MongoDB实现ResultFul风格接口
  10. 编写一个java_Java入门篇(一)——如何编写一个简单的Java程序
  11. 认证资料大全(八)------ SUN认证列表
  12. Android 图片压缩之多种压缩方式结合使用,阿里Android开发面试解答
  13. 无法软关机(关机变重启或关机不切断电源而显示:您可以安全关机)解决方法+ACPI精解...
  14. HTTP状态码1xx到5xx
  15. Elliptical Grid Mapping(椭圆映射法)
  16. 新电脑改win7系统如何调整bois的方法总结
  17. linux远程部分文件传输不了,远程传输文件(linux之scp简单操作)
  18. 回车键换行符回车符 朦胧中!
  19. win10计算机跑分,Win10使用鲁大师对显卡跑分测试时出现FPS锁在60帧如何解决
  20. 基于AidLux平台实现智慧社区中高空抛物和车辆车牌识别

热门文章

  1. 表达提交返回信息_盘锦市2020年义务教育阶段招生入学信息服务平台家长填报指南...
  2. 文本分类模型_文本分类模型之TextCNN
  3. python中raw_input未定义_python之NameError: name 'raw_input' is not defined
  4. 双下划线开头的变量实现了类私有变量功能
  5. java调试生命周期,一种基于JAVA的智能合约生命周期的管理方法与流程
  6. sql能查到数据 dataset对象里面没有值_新零售数据分析报告
  7. 买游戏来运营_「笔吧评测室」双十一快来了,买游戏本要做好心理准备
  8. qt 中给 按纽(button) 填充图片的方法
  9. 天翼云从业认证(1.2)存储的概念、体系结构、块存储、对象存储、文件存储以及 RAID 磁盘管理技术
  10. gradle打包 执行类方法