<!DOCTYPE html> <html> <head> <title>撤销命令</title> </head> <body> <div id="ball" style="position:absolute;background:#000;50px;height:50px"></div> 输入小球移动后的位置:<input id="pos"/> <button id="moveBtn">开始移动</button> <button id="cancelBtn">cancel</cancel> <!--增加取消按钮--> </body> <script type="text/javascript"> //运动算法 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的初始位置 this.endPos = 0;//dom的目标位置 this.propertyName = null;//dom节点需要被改变的css属性名 this.easing = null;//缓动算法 this.duration = null;//动画持续时间 } Animate.prototype.start = function (propertyName, endPos, duration, easing) { this.startTime = +new Date;//动画启动时间 this.startPos = this.dom.getBoundingClientRect()[propertyName];//初始位置 this.propertyName = propertyName; this.endPos = endPos; this.duration = duration; this.easing = tween[ easing ]; // 缓动算法 var _self = this; var timeId = setInterval(function () {//启动定时器;开始动画 if (_self.step() === false) {//如果动画结束;停止计时器 clearInterval(timeId); } },19) }; Animate.prototype.step = function () { var t = +new Date; if (t>=this.startTime+this.duration) { this.update(this.endPos); return false; } //当前位置 var pos = this.easing(t - this.startTime, this.startPos, this.endPos - this.startPos, this.duration); this.update(pos); console.log(pos); } Animate.prototype.update = function (pos) { this.dom.style[this.propertyName] = pos+'px'; } //此处开始,上面引用 /* var ball = document.getElementById('ball'), pos = document.getElementById('pos'), moveBtn = document.getElementById('moveBtn'); moveBtn.onclick = function () { var animate = new Animate(ball); animate.start('left', pos.value, 1000, 'strongEaseOut'); } */ /* 如果文本框输入 200,然后点击 moveBtn按钮,可以看到小球顺利地移动到水平方向 200px 的位置。现在我们需要一个方法让小球还原到开始移动之前的位置。当然也可以在文本框中再次 输入-200,并且点击 moveBtn 按钮,这也是一个办法,不过显得很笨拙。页面上最好有一个撤销 按钮,点击撤销按钮之后,小球便能回到上一次的位置。 */ //改成命令模式 var ball = document.getElementById('ball'), pos = document.getElementById('pos'), moveBtn = document.getElementById('moveBtn'), cancelBtn = document.getElementById( 'cancelBtn' ); var MoveCommond = function (receiver, pos) { this.receiver = receiver; this.pos = pos; this.oldPos = null; }; MoveCommond.prototype.execute = function () { this.receiver.start('left', this.pos, 1000, 'strongEaseOut'); //Element.getBoundingClientRect()方法返回元素的大小及其相对于视口的位置。 /* 返回值是一个 DOMRect 对象,这个对象是由该元素的 getClientRects() 方法返回的一组矩形的集合, 即:是与该元素相关的CSS 边框集合 。 DOMRect 对象包含了一组用于描述边框的只读属性——left、top、right和bottom,单位为像素。除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。 */ //记录开始移动前的位置; this.oldPos = this.receiver.dom.getBoundingClientRect()[this.receiver.propertyName]; } MoveCommond.prototype.undo = function () { //回到记录的位置 this.receiver.start('left', this.oldPos, 1000, 'strongEaseOut'); } var moveCommond; moveBtn.onclick = function () { var animate = new Animate(ball); moveCommond = new MoveCommond(animate, pos.value); moveCommond.execute(); } cancelBtn.onclick = function () { moveCommond.undo();//撤销命令 } </script> </html>