編輯器等工具類如何設計使用者操作歷史記錄佇列實現前進後退
阿新 • • 發佈:2021-07-25
越來越多的前端用於編輯器類工具的開發,常見的如富文字編輯器、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;
};