使用命令模式实现撤销的需求

271 阅读1分钟
<body>
    <div style="background: rgb(228, 170, 180); width: 1000px;height: 50px"></div>
    <div style="background: rgb(228, 109, 130); width: 1000px;height: 50px"></div>
    <div style="background: rgb(233, 50, 80); width: 1000px;height: 50px"></div>
    <div style="background: rgb(236, 9, 51); width: 1000px;height: 50px"></div>
    <div id="ball" style="position:absolute;top: 50px; left: 100px;background:rgb(21, 109, 43);width:50px;height:50px"></div>

    <div>   
        输入小球要移动的方向:<input id="prop"/>     
        输入小球移动后的位置:<input id="pos"/>
        <button id="moveBtn">开始移动</button>
        <button id="cancelBtn">撤销</button>
    </div>
   <script>
       const tween = {
           linear: function(allDistance, duration, time, startPos) {
                return allDistance/duration*time + startPos
           }
       }
       const Animate = function(dom) {
           this.dom = dom;
           this.startTime = 0; // 动画开始时间
           this.duration = 0;
           this.endPos = 0;
           this.startPos = 0;
           this.easing = null;
           this.propertyName = null
       }
       Animate.prototype.start = function(propertyName, endPos, duration, easing) {
           this.startTime = +new Date;
           this.duration = duration
           this.endPos = endPos
           this.startPos = this.dom.getBoundingClientRect()[propertyName];
           this.easing = tween[easing]
           this.propertyName = propertyName
           let timer = setInterval(() => {
               console.log('timer')
                if (this.step() === false) {
                    clearInterval(timer)
                    timer = null
                }
           }, 19)
       }
       Animate.prototype.step = function() {
           const now = +new Date;
           if (now >= this.startTime + this.duration) {
               this.update(this.endPos)
               return false
           }
           const pos = this.easing(this.endPos - this.startPos, this.duration, now - this.startTime, this.startPos)
           this.update(pos)
       }
       Animate.prototype.update = function(pos) {
           this.dom.style[ this.propertyName ] = pos + 'px';
       }


        var ball = document.getElementById( 'ball' );
        var pos = document.getElementById( 'pos' );
        var prop = document.getElementById( 'prop' );
        var moveBtn = document.getElementById( 'moveBtn' );
        var cancelBtn = document.getElementById( 'cancelBtn' );
        let moveCommand = null
        // moveBtn.onclick = function(){
        //     var animate = new Animate( ball );
        //     animate.start( prop.value || 'left', pos.value, 1000, 'linear' );
        // }
        // 改成命令模式
        const MoveCommand = function() {
            this.receiver = Array.prototype.shift.call(arguments)
            this.args = arguments
            this.oldArgs = null
        }
        MoveCommand.prototype.execute = function() {
            this.receiver.start(...this.args);
            const argsCopy = [...this.args]
            Array.prototype.splice.call(argsCopy, 1, 1, this.receiver.startPos)
            this.oldArgs = argsCopy
        }
        MoveCommand.prototype.undo = function() {
            this.receiver.start(...this.oldArgs);
        }
        moveBtn.onclick = function(){
            var animate = new Animate( ball );
            moveCommand = new MoveCommand(animate, prop.value || 'left', pos.value, 1000, 'linear')
            moveCommand.execute();
        } 
        cancelBtn.onclick = function() {
            moveCommand.undo();
        }
   </script>
</body>