创新实训:动作队列与动作类

撤销是将上一个动作造成的影响消除,而重做是将消除的动作的影响重新添加。

撤销和重做通过两个队列进行管理。分为actionQueue和backQueue

当进行撤销动作的时候,将actionQueue的末尾action弹出到backQueue的末尾中,并执行撤销动作

当进行重做动作的时候,将backQueue的末尾action弹出到actionQueue的末尾中,并执行重做动作

需要注意的是,有的动作不会产生影响,所以不应加入队列,有的动作为连续多次发生,如果全部加入到队列当中,可能对功能造成极大的影响,所以需要进行合并操作。

所以首先进行队列类的设计和action类的设计。

1.队列类的设计

actionQueue和backQueue作为队列管理

filter筛去不应进入队列的类

pushAction将action推入actionQueue中

逻辑为,首先将action的后续操作做完,然后发出P信号允许下一个信号发出,同时使用filter筛去动作,判断上一个动作和这一个是否可以合并,清空backQueue,并发出discard信号

forwardAction和backAction首先判断队列中是否有action,然后进行撤销动作

updateMapAction用来更新ui按钮状态。

class ActionQueue {
    constructor() {
        this.actionQueue=[];
        this.backQueue=[];
    }

    filter(action){
       //筛去不进入队列的action
    }
    pushAction(action){
        action.after();
        V();
        if(this.filter(action)) {
            if(this.actionQueue.length>0){
                if(action.merge(this.actionQueue[this.actionQueue.length-1])){
                    this.actionQueue.pop();
                }
            }
            this.actionQueue.push(action);
            if(this.backQueue.length>0){
                let time=getActionCounter();
                P("discard",{time:time});
            }
            this.backQueue=[];
        }
        this.updateMapAction()
    }

    backAction(){
        if(this.actionQueue.length>0){
            console.log(this.actionQueue);
            console.log(this.backQueue);
            this.actionQueue[this.actionQueue.length-1].backward();
            this.backQueue.push(this.actionQueue[this.actionQueue.length-1]);
            this.actionQueue.pop();
            // console.log(this.actionQueue);
            // console.log(this.backQueue);
        }
        this.updateMapAction();

    }

    forwardAction(){
        if(this.backQueue.length>0){
            console.log(this.actionQueue);
            console.log(this.backQueue);
            this.backQueue[this.backQueue.length-1].forward();
            this.actionQueue.push(this.backQueue[this.backQueue.length-1]);
            this.backQueue.pop();
            // console.log(this.actionQueue);
            // console.log(this.backQueue);
        }
        this.updateMapAction();
    }

    updateMapAction(){
        let msg={}
        msg['back']=this.actionQueue.length !== 0;
        msg['up']=this.backQueue.length !== 0;
        color_change_bar(msg);
    }
}

2.action类的设计

action类统一继承base_action类,方法有

create:向后端发送信号

after:在后端执行完成之后需要进行的操作

forward:重做操作

backward:撤销操作

merge:合并操作,是否选择和actionQueue的前一个动作进行合并,优化用户体验。

fliter:过滤器,过滤不需要记录在actionQueue队列中的操作

3.对于各个类的设计逻辑

A.不进入actionQueue队列中的类

这些类不需要进入队列,只需设计after操作即可

B.需要进入actionQueue队列中的类但无需记录状态

如move和movep,只需要根据信号进行反推即可

C.需要进入actionQueue队列中的类且需记录状态

如set_html,set_theta,直接设置值的操作,需要进行类的设计

4.对部分类的逻辑解读

A.back\backward\front\forward

置底置顶操作入队列,但是撤销和重做的实现较为简单。

Eg:baskAction

after(){
    this.core=getCoreList();
}

forward(){
    P("cursors",{ids:this.core},false);
    P("back",{},false);
}

backward(){
    P("cursors",{ids:this.core},false);
    P("front",{},false);
}
B.getP

取得关键点的after函数有许多重要的部分

after(){
    setCore(this.points)
    changeModel();
    P("get_center",{})
}

首先通过points进行关键点的绘制和coreQueue的确认和维护

changeModel为右侧工具栏的切换

get_center为得到组件的旋转中心进行设置。

C.get_center
after(){
    let coreList=getCoreList();
    console.log(coreList);
    for(let i=0;i<coreList.length;i++){
        let element=getModuleByGid(coreList[i]);
        element.center_x=this.msg['centers'][i]['x']
        element.center_y=this.msg['centers'][i]['y']
        setTreeSonCenter(coreList[i],{x:this.msg['centers'][i]['x'],y:this.msg['centers'][i]['y']})
    }

}

center从组件树上往下对组件进行center赋值

D.move和moveP
after(){
    for(let i=0;i<this.ids.length;i++){
        updateGuide(this.ids[i])
        guideSet(this.ids[i])
        update_position_by_gid(this.ids[i])
        let list=getChildren(this.ids[i])
        for(let j=0;j<list.length;j++){
            update_position_by_gid(list[j])
        }
    }
}

位置状态改变后,需要更新引导线的位置,锚点的位置,同时children的锚点也要进行修改

merge(action){
    // console.log(action);
    if(action.type==="move"){
        let map={}
        for(let i=0;i<this.ids.length;i++){
            map[this.ids[i]]=1;
        }
        for(let i=0;i<action.ids.length;i++){
            if(map[action.ids[i]]===undefined)return false;
        }
        this.dx+=action.dx;
        this.dy+=action.dy;
        return true;
    }
    else return false;
}

merge操作需要查看操作list是否完全一致,如果完全一致,则进行合并

E.get_rect/get_html/get_alignment/get_spacing/get_theta

需要用在右侧的状态栏更新,所以需要进行一定的设置

Eg:get_rect

after(){
    let msg={}
    msg['flag']=true;
    msg['x']=this.msg['rect']['x']
    msg['y']=this.msg['rect']['y']
    msg['height']=this.msg['rect']['height']
    msg['width']=this.msg['rect']['width']
    setPosition(msg);
    setElementSize(msg);
}
F.add

这应该是最为复杂的动作设计,需要区分line,group生成的伪矩阵。

after(){
    this.msg['show']=this.cmd['show'];
    if(!this.cmd['show']){
        let node=document.getElementById(this.id);
        let parser=new cssParser();
        parser.parseStyle(node.getAttribute("style"))
        parser.updateStyle({display:"none"})
        parser.updateStyle({'stroke-width':0})
        node.setAttribute("style",parser.get());
        createModule(this.msg,false);
        addModuleToTree(this.id,this.type);
        let coreList=getCoreList();
        for (var i=0;i<coreList.length;i++){
            linkByGroup(coreList[i],this.id);
        }
        P("cursors",{ids:[this.msg['id']]})
        P("cover_children",{})
        P("enable_scale_bind",{})
        return;
    }
    if(this.type==="line"){
        createLine(this.msg);
    }else{
        createModule(this.msg);//需要修改
    }
    P("cursors",{ids:[this.msg['id']]})
    let move=set_move_center();
    let bbox=document.getElementById(this.id).getBBox();
    let msg={g_id:getCoreList()[0],move_x:move['x']-bbox.width/2,move_y:move['y']-bbox.height/2}
    P("move",msg,false)
    addModuleToTree(this.id,this.type);
    // anchor_add(this.id);
}
G.cursors

cursors选中后只需get_p即可

5.action流程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值