在web應用中,用戶在進行一些富交互行爲的操做時不免會出現誤操做,好比在富文本編輯器設置錯了字體顏色就須要撤回,作H5活動頁面的時候不當心刪了一個圖片也須要撤回,更好比在線設計原型圖應用的時候不當心刪了一個頁面等,總之在交互場景很是複雜的狀況下,用戶操做失誤的可能性很是大,這時候‘撤銷’和‘前進’這兩個操做就頗有必要了,並且用戶體驗也很好
無論是任何場景下的web應用,用戶的每一次操做咱們均可以當作是對某個組件或某個對象的狀態和屬性進行改變,一旦連續的動做操做完成正準備進行下一個動做以前,此刻的狀態就是一個全新的狀態javascript
A —— B —— C
用戶未操做的時候全局狀態是A
用戶操做某個組件使其移動到位置X,鬆開鼠標以後全局狀態是B
用戶操做另外一個組件使其刪除,完成後全局狀態是C
因此,撤銷的操做就是在用戶操做狀態到C的時候讓全局的狀態回到B,回到上一次操做完的時候。
那麼就須要能夠存放這種大量狀態的列表或索引來記錄每一次操做的動做java
但若是我用某一個數組變量來存儲如此龐大的數據是否是略顯不妥?數據量越大內存應該會爆吧?因此這裏我推薦你們使用IndexedDB
下面是利用Angular、Rxjs和IndexedDB封裝好的一個服務類web
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 = -1 this.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 -= 1 throw 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 -= 1 this.db.getByKey('panelItem', this.dbCurrentIndex).then(res=>{ this.launchDBDataValue$.next(res) },()=>{ }) } } /** * 執行前進操做發射DB數據集 */ public acquireMoveDBData(): void { if( this.isMove ) { this.dbCurrentIndex += 1 this.db.getByKey('panelItem', this.dbCurrentIndex).then(res => { this.launchDBDataValue$.next(res) }, () => { }) } } /** * 清除DB集合panelItem */ public dbClear(): void { this.db.clear('panelItem').then(_e => {}) } }
這裏我偷懶了一下,直接採用自增的id做爲key了,也方便查找
每一次操做所存儲的數據以下數據庫
最後能夠看一下我實現好了的撤銷和前進操做的場景數組