编辑器等工具类项目如何设计用户操做历史记录队列实现撤销与反撤销

愈来愈多的前端用于编辑器类工具的开发,常见的如富文本编辑器、H5页面生成器、低代码平台etc... 对于这类编辑器的工具除去ctrl+c ctrl+v外 ,通常还须要有ctrl+z ctrl+y的功能。如何设计一个用户历史记录的队列才能更好的实现用户编辑的前进后退前端

1、历史记录是保存操做仍是保存当前所有数据状态?

就是说假设用户有以下操做数组

未命名文件.jpg

咱们是记录为浏览器

['a', 'ab' , 'abc']markdown

仍是app

[ {type: 'add" value: 'a'}, {type: 'add" value: 'b'}, {type: 'add" value: 'c'}, ]编辑器

一、保存方式的选择

对于这个问题要看具体的项目类型,如单一类型的操做,数据量不大,能够直接保存每一步的操做时的所有数据状态,如简单的文本编辑器,而对于操做复杂,数据量大咱们选用方式二。工具

二、方式二的实现

对于方式二,咱们我看根据项目的操做自定义操做类型,如咱们是一个H5编辑器,咱们能够分类为:ui

  • type: page -页面的操做, 记录当前页面的数据信息
  • type: pageProps -记录单个页面的属性信息
  • type: component -记录某个页面下的所有组件
  • type: componentProps -记录某个组件的属性

在用户后退或前进时,咱们能够根据上一步修改的数据,进行对应的恢复,或者逆向修改;this

2、历史记录队列的建立与添加

肯定了记录的内容,如何建立一个历史记录? 什么时间添加呢? 当前的状态又如何指向呢?url

一、队列的建立

首先咱们建立一个历史记录类,用一个数组保存数据,用一个变量为指针,指向用户当前的最新操做

export default class History {
  constructor() {
    this.historyStore = []
    this.historyIndex = -1
    this.max = 100;//最大记录数目
  }
 }

复制代码

二、指针的指向

假设用户有以下操做:

step action
1 a
2 b
3 c
4 d
5 back
6 back
7 e
8 f

1)指针默认指向用户当前的最新操做

187690f3-1236-4e7d-a4f8-0ed7ec35c850.png

2)用户后退,指针后退

28afbbee-a78b-49c9-8047-286151a7d182.png

3)后退后再添加记录,删除当前指针后面的元素,再添加新的记录

e2e947ae-9225-4d7d-bde7-b2044afda55a.png

代码实现为

// 新增记录
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 ++
}

复制代码

三、添加记录的时间节点

对于用户的操做,咱们能够再数据更新前添加未更新前数据到记录,也能够在更新后记录后新后的数据

如用户有如下操做

step action
1 a
2 b
3 c
4 back

(1)若是咱们记录更新前的数据,到step 4时咱们有以下数据记录

第一次后退时,需记录当前最后一步更新后的数据,以保证能前进到最新数据

660b4447-f4bf-4889-b3b6-f43333cce01c.png

(2)若是咱们更新后得数据则须要在第一步时,则需有

  • 在第一次添加记录前,先记录一次未修改前的所有数据

以确保后退到开始时候,有最初数据

8et3vzsp2.png

这样每次咱们后退时还须判断更新的记录,肯定恢复的数据,

3、快捷键的实现

//按键摁下记录各个特殊键
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;
};
复制代码
相关文章
相关标签/搜索