编辑器等工具类如何设计用户操作历史记录队列实现前进后退
越来越多的前端用于编辑器类工具的开发,常见的如富文本编辑器、H5页面生成器、低代码平台etc... 对于这类编辑器的工具除去ctrl+c ctrl+v外 ,一般还需要有ctrl+z ctrl+y的功能。如何设计一个用户历史记录的队列才能更好的实现用户编辑的前进后退
一、历史记录是保存操作还是保存当前全部数据状态?
就是说假设用户有如下操作
我们是记录为
['a', 'ab' , 'abc']
还是
[ {type: 'add" value: 'a'}, {type: 'add" value: 'b'}, {type: 'add" value: 'c'}, ]
1、保存方式的选择
对于这个问题要看具体的项目类型,如单一类型的操作,数据量不大,可以直接保存每一步的操作时的全部数据状态,如简单的文本编辑器,而对于操作复杂,数据量大我们选用方式二。
2、方式二的实现
对于方式二,我们我看根据项目的操作自定义操作类型,如我们是一个H5编辑器,我们可以分类为:
- type: page -页面的操作, 记录当前页面的数据信息
- type: pageProps -记录单个页面的属性信息
- type: component -记录某个页面下的全部组件
- type: componentProps -记录某个组件的属性
在用户后退或前进时,我们可以根据上一步修改的数据,进行对应的恢复,或者逆向修改;
二、历史记录队列的创建与添加
确定了记录的内容,如何创建一个历史记录? 什么时间添加呢? 当前的状态又如何指向呢?
1、队列的创建
首先我们创建一个历史记录类,用一个数组保存数据,用一个变量为指针,指向用户当前的最新操作
export default class History {
constructor() {
this.historyStore = []
this.historyIndex = -1
this.max = 100;//最大记录数目
}
}
复制代码
2、指针的指向
假设用户有如下操作:
step | action |
---|---|
1 | a |
2 | b |
3 | c |
4 | d |
5 | back |
6 | back |
7 | e |
8 | f |
1)指针默认指向用户当前的最新操作
2)用户后退,指针后退
3)后退后再添加记录,删除当前指针后面的元素,再添加新的记录
代码实现为
// 新增记录
addItem(d) {
// 撤销后重新添加记录 删除撤销的记录
if(this.historyIndex != this.historyStore.length - 1){
let dif = this.historyStore.length - this.historyIndex - 1
this.historyStore.splice(this.historyIndex, dif)
}
// 新增记录
this.historyStore.push(d)
this.historyIndex ++ ;
// 超出
if(this.historyIndex.length > this.max){
this.historyStore.shift()
this.historyIndex --
}
}
// 后退
back() {
if(this.historyStore.length == 0 || this.historyIndex < 0) return;
this.historyIndex --
}
// 前进
go() {
if(this.historyStore.length == 0 || this.historyIndex >= this.historyStore.length) return;
this.historyIndex ++
}
复制代码
3、添加记录的时间节点
对于用户的操作,我们可以再数据更新前添加未更新前数据到记录,也可以在更新后记录后新后的数据
如用户有以下操作
step | action |
---|---|
1 | a |
2 | b |
3 | c |
4 | back |
(1)如果我们记录更新前的数据,到step 4时我们有如下数据记录
第一次后退时,需记录当前最后一步更新后的数据,以保证能前进到最新数据
(2)如果我们更新后得数据则需要在第一步时,则需有
- 在第一次添加记录前,先记录一次未修改前的全部数据
以确保后退到开始时候,有最初数据
这样每次我们后退时还须判断更新的记录,确定恢复的数据,
三、快捷键的实现
//按键摁下记录各个特殊键
let ctrlDown = false;
window.addEventListener('keydown', function (e) {
if (['Control', 'Meta'].includes(e.key)) {
ctrlDown = true;
}
if(ctrlDown && e.key == 'Z') //后退。。。
if(ctrlDown && e.key == 'Y') //前进。。。
})
// 松开按键
window.addEventListener('keyup', function (e) {
if (['Control', 'Meta'].includes(e.key)) {
ctrlDown = false;
}
})
// 浏览器脱离焦点,释放
window.onblur = function() {
ctrlDown = false;
};
原文地址:https://www.cnblogs.com/pangys/p/15058472.html
- 韩国全球首测5G网络下自动驾驶 为汽车安全保驾护航的竟是路灯
- 通过与Quickbuild和Mist.io的持续集成实现云管理和使用监控
- .NET Core的文件系统[1]:读取并监控文件的变化
- ASP.NET MVC以ValueProvider为核心的值提供系统: ValueProviderFactory
- 云本机应用程序成熟度的模型
- 如何利用ETW(Event Tracing for Windows)记录日志
- 如何利用ETW(Event Tracing for Windows)记录日志
- 如何利用ETW(Event Tracing for Windows)记录日志
- ASP.NET MVC以ValueProvider为核心的值提供系统: DictionaryValueProvider
- ASP.NET MVC如何实现自定义验证(服务端验证+客户端验证)
- .NET Core的文件系统[2]:FileProvider是个什么东西?
- Python多线程怎样优雅的响应中断异常
- .NET Core的文件系统[3]:由PhysicalFileProvider构建的物理文件系统
- .NET Core的文件系统[4]:由EmbeddedFileProvider构建的内嵌(资源)文件系统
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法