富交互Web应用中的撤销和前进

在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了,也方便查找
每一次操做所存储的数据以下数据库

clipboard.png

最后能够看一下我实现好了的撤销和前进操做的场景数组

clipboard.png

相关文章
相关标签/搜索