命令模式

/**
 * Created by shangkuikui on 2017/4/26.
 */

var tween = {
    linear: function (t, b, c, d) {
        return c * t / d + b;
    },
    easeIn: function (t, b, c, d) {
        return c * ( t /= d ) * t + b;
    },
    strongEaseIn: function (t, b, c, d) {
        return c * ( t /= d ) * t * t * t * t + b;
    },
    strongEaseOut: function (t, b, c, d) {
        return c * ( ( t = t / d - 1) * t * t * t * t + 1 ) + b;
    },
    sineaseIn: function (t, b, c, d) {
        return c * ( t /= d) * t * t + b;
    },
    sineaseOut: function (t, b, c, d) {
        return c * ( ( t = t / d - 1) * t * t + 1 ) + b;
    }
};

var Animate = function (dom) {
    this.dom = dom; // 进行运动的dom 节点
    this.startTime = 0; // 动画开始时间
    this.startPos = 0; // 动画开始时,dom 节点的位置,即dom 的初始位置
    this.endPos = 0; // 动画结束时,dom 节点的位置,即dom 的目标位置
    this.propertyName = null; // dom 节点需要被改变的css 属性名
    this.easing = null; // 缓动算法
    this.duration = null; // 动画持续时间
};

Animate.prototype.start = function (propertyName, endPos, duration, easing, cb) {
    this.startTime = +new Date; // 动画启动时间
    this.startPos = this.dom.getBoundingClientRect()[propertyName]; // dom 节点初始位置
    this.propertyName = propertyName; // dom 节点需要被改变的CSS 属性名
    this.endPos = endPos; // dom 节点目标位置
    this.duration = duration; // 动画持续事件
    this.easing = tween[easing]; // 缓动算法
    var self = this;
    var timeId = setInterval(function () { // 启动定时器,开始执行动画
        if (self.step() === false) { // 如果动画已结束,则清除定时器
            clearInterval(timeId);
            Event.trigger('finish');
            if(cb && typeof cb === "function" ){
                cb();
            }
        }
    }, 19);
};

Animate.prototype.step = function () {
    var t = +new Date; // 取得当前时间
    if (t >= this.startTime + this.duration) { // (1)
        this.update(this.endPos); // 更新小球的CSS 属性值
        return false;
    }
    var pos = this.easing(t - this.startTime, this.startPos, this.endPos - this.startPos, this.duration);
// pos 为小球当前位置
    this.update(pos); // 更新小球的CSS 属性值
};

Animate.prototype.update = function( pos ){
    this.dom.style[ this.propertyName ] = pos + 'px';
};
View Code
var Event = (function () {
    var global = this,
        Event,
        _default = 'default';
    Event = function () {
        var _listen,
            _trigger,
            _remove,
            _slice = Array.prototype.slice,
            _shift = Array.prototype.shift,
            _unshift = Array.prototype.unshift,
            namespaceCache = {},
            _create,
            find,
            each = function (ary, fn) {
                var ret;
                for (var i = 0, l = ary.length; i < l; i++) {
                    var n = ary[i];
                    ret = fn.call(n, i, n);
                }
                return ret;
            };
        _listen = function (key, fn, cache) {
            if (!cache[key]) {
                cache[key] = [];
            }
            cache[key].push(fn);
        };
        _remove = function (key, cache, fn) {
            if (cache[key]) {
                if (fn) {
                    for (var i = cache[key].length; i >= 0; i--) {
                        if (cache[key][i] === fn) {
                            cache[key].splice(i, 1);
                        }
                    }
                } else {
                    cache[key] = [];
                }
            }
        };
        _trigger = function () {
            var cache = _shift.call(arguments),
                key = _shift.call(arguments),
                args = arguments,
                _self = this,
                ret,
                stack = cache[key];
            if (!stack || !stack.length) {
                return;
            }
            return each(stack, function () {
                return this.apply(_self, args);
            });
        };
        _create = function (namespace) {
            var namespace = namespace || _default;
            var cache = {},
                offlineStack = [], // 离线事件
                ret = {
                    listen: function (key, fn, last) {
                        _listen(key, fn, cache);
                        if (offlineStack === null) {
                            return;
                        }
                        if (last === 'last') {
                            offlineStack.length && offlineStack.pop()();
                        } else {
                            each(offlineStack, function () {
                                this();
                            });
                        }
                        offlineStack = null;
                    },
                    one: function (key, fn, last) {
                        _remove(key, cache);
                        this.listen(key, fn, last);
                    },
                    remove: function (key, fn) {
                        _remove(key, cache, fn);
                    },
                    trigger: function () {
                        var fn,
                            args,
                            _self = this;
                        _unshift.call(arguments, cache);
                        args = arguments;
                        fn = function () {
                            return _trigger.apply(_self, args);
                        };
                        if (offlineStack) {
                            return offlineStack.push(fn);
                        }
                        return fn();
                    }
                };
            return namespace ?
                ( namespaceCache[namespace] ? namespaceCache[namespace] :
                    namespaceCache[namespace] = ret )
                : ret;
        };
        return {
            create: _create,
            one: function (key, fn, last) {
                var event = this.create();
                event.one(key, fn, last);
            },
            remove: function (key, fn) {
                var event = this.create();
                event.remove(key, fn);
            },
            listen: function (key, fn, last) {
                var event = this.create();
                event.listen(key, fn, last);
            },
            trigger: function () {
                var event = this.create();
                event.trigger.apply(this, arguments);
            }
        };
    }();
    return Event;
})();
View Code

以上是文章中用到的两个js文件,event.js  和  animate.js

 

 

1普通青年版(初级命令模式)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<button id="bt1">按钮1</button>
<button id="bt2">按钮2</button>
<button id="bt3">按钮3</button>
<div style="position:absolute;background:blue" id="div">我是div</div>
</body>
<script src="animate.js"></script>
<script>
    const bt1  = document.querySelector('#bt1');
    const bt2  = document.querySelector('#bt2');
    const bt3  = document.querySelector('#bt3');
    const func= {
        fn1:function () {
            var div = document.getElementById( 'div' );
            var animate = new Animate( div );
            animate.start( 'left', 500, 1000, 'sineaseIn' );
            console.log('fn1')
        },
        fn2:function () {
            console.log('fn2')
        },
        fn3:function () {
            console.log('fn3')
        }
    };
    let Acommond = function (recevier) {
        return{
            execute () {
                recevier.fn1()
            }
        }
    }
    let Bcommond = function (recevier) {
        return{
            execute () {
                recevier.fn2()
            }
        }
    }
    let acommond = new Acommond(func);
    let bcommond = new Bcommond(func);
    const  setCommand= function (bt,commond) {
        bt.onclick= function () {
            commond.execute();
        }
    }
    setCommand(bt1,acommond);
    setCommand(bt2,bcommond);



</script>
</html>
View Code

 

2文艺青年版(+撤销功能)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <button id="moveBtn">移动</button>
    <button id="cancelBtn">撤销</button>
    <input type="text" id="pos">
    <div id="ball" style="position:absolute;background:#000;width:50px;height:50px"></div>
</body>

<script src="animate.js"></script>
<script>
    const ball = document.getElementById( 'ball' );
    const pos = document.getElementById( 'pos' );
    const moveBtn = document.getElementById( 'moveBtn' );
    const cancelBtn = document.getElementById( 'cancelBtn' );


    /*moveBtn.onclick = function(){
        var animate = new Animate( ball );
        animate.start( 'left', pos.value, 1000, 'strongEaseOut' );
    };*/

    const  setCommand= function (tar,commond) {
        tar.onclick= function () {
            commond.execute();
        }
    }


    const MoveCommand = function( receiver, pos ){
        this.receiver = receiver;
        this.pos = pos;
        this.oldpos = null;
    };
    MoveCommand.prototype.execute = function(){
        //let that  = this;
        this.receiver.start( 'left', this.pos, 1000, 'strongEaseOut' );
        this.oldPos = this.receiver.dom.getBoundingClientRect()[ this.receiver.propertyName ];
    };

    MoveCommand.prototype.undo = function(){
        this.receiver.start( 'left', this.oldPos, 1000, 'strongEaseOut' );
    // 回到小球移动前记录的位置
    };

    //setCommand(moveBtn,moveCommand);
    let moveCommand;
    moveBtn.onclick =function () {
        const animate = new Animate(ball);
        moveCommand = new MoveCommand(animate , pos.value);
        moveCommand.execute();
    }
    cancelBtn.onclick =function () {
        moveCommand.undo();
    }
</script>
</html>
View Code

3文艺青年豪华版(+回放(队列功能)模式,使用回调方式通知)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<button id="moveBtn">移动</button>
<button id="cancelBtn">撤销</button>
<button id="reply">播放</button>
<input type="text" id="pos">
<div id="ball" style="position:absolute;background:#000;width:50px;height:50px"></div>
</body>
<script src="animate.js"></script>
<script>
    const ball = document.getElementById('ball');
    const pos = document.getElementById('pos');
    const moveBtn = document.getElementById('moveBtn');
    const cancelBtn = document.getElementById('cancelBtn');
    const replyBtn = document.getElementById('reply');


    const MoveCommand = function (receiver, pos) {
        this.receiver = receiver;
        this.pos = pos;
        this.oldpos = null;
    };
    MoveCommand.prototype.execute = function (cb) {
        //let that  = this;
        this.receiver.start('left', this.pos, 1000, 'strongEaseOut', cb);
        this.oldPos = this.receiver.dom.getBoundingClientRect()[this.receiver.propertyName];
    };

    let commandStack = []; // 保存命令的堆栈

    MoveCommand.prototype.undo = function () {
        this.receiver.start('left', this.oldPos, 1000, 'strongEaseOut');
        // 回到小球移动前记录的位置
    };


    let moveCommand;
    moveBtn.onclick = function () {
        const animate = new Animate(ball);
        moveCommand = new MoveCommand(animate, pos.value);
        moveCommand.execute();
        commandStack.push(moveCommand);
        //console.log(commandStack);
    }
    cancelBtn.onclick = function () {
        moveCommand.undo();
    }

    //目前作为演示 只是做了三个函数回放的演示
    replyBtn.onclick = function () {
        let commond;
        commond = commandStack.shift();
        commond.execute(function () {
            commond = commandStack.shift();
            commond.execute(function () {
                commond = commandStack.shift();
                commond.execute(function () {

                })
            })
        });
    }
</script>
</html>
View Code

4文艺青年豪华加长版(+回放(队列功能)模式,使用广播方式通知)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<button id="moveBtn">移动</button>
<button id="cancelBtn">撤销</button>
<button id="reply">播放</button>
<input type="text" id="pos">
<div id="ball" style="position:absolute;background:#000;width:50px;height:50px"></div>
</body>
<script src="Event.js"></script>
<script src="animate.js"></script>
<script>
    const ball = document.getElementById('ball');
    const pos = document.getElementById('pos');
    const moveBtn = document.getElementById('moveBtn');
    const cancelBtn = document.getElementById('cancelBtn');
    const replyBtn = document.getElementById('reply');


    const MoveCommand = function (receiver, pos) {
        this.receiver = receiver;
        this.pos = pos;
        this.oldpos = null;
    };
    MoveCommand.prototype.execute = function (cb) {
        //let that  = this;
        this.receiver.start('left', this.pos, 1000, 'strongEaseOut', cb);
        this.oldPos = this.receiver.dom.getBoundingClientRect()[this.receiver.propertyName];
    };

    let commandStack = []; // 保存命令的堆栈
    let historyStack = []; // 保存命令的历史堆栈

    MoveCommand.prototype.undo = function () {
        this.receiver.start('left', this.oldPos, 1000, 'strongEaseOut');
        // 回到小球移动前记录的位置
    };


    let moveCommand;
    moveBtn.onclick = function () {
        const animate = new Animate(ball);
        moveCommand = new MoveCommand(animate, pos.value);

        commandStack.push(moveCommand);
        historyStack.push(moveCommand);
    }
    cancelBtn.onclick = function () {
        moveCommand.undo();
    }

    replyBtn.onclick = function () {
        historyloop(historyStack);
    }

    /**
     *  用于时刻从命令队列里取出命令执行
     * @param Stack  命令队列
     */
    function eventloop(Stack) {
        let continued = true;
        setInterval(function () {
            if (continued) {
                let tempcommond = Stack.shift();
                tempcommond && tempcommond.execute();
                tempcommond && (continued = false);
            }

        }, 10);
        Event.listen('finish', function () {
            continued = true;
            console.log('finish')
        })
    };
    eventloop(commandStack);


    /**
     *  用于播放历史命令队列
     * @param Stack  命令队列
     */
    function historyloop(Stack) {
        let continued = true;
        let timer = setInterval(function () {
            if (continued) {
                let tempcommond = Stack.shift();
                tempcommond && tempcommond.execute();
                tempcommond && (continued = false);
                if(!tempcommond){
                    clearInterval(timer);
                }
            }
        }, 100);
        Event.listen('finish', function () {
            continued = true;
            console.log('finish')
        })
    }
</script>
</html>
View Code

 

posted @ 2017-04-28 10:32  _白马非马  阅读(216)  评论(0编辑  收藏  举报