JavaScript设计模式 - 策略模式
策略模式: 定义一系列算法,把他们一个一个封装起来,并且使他们可以相互替换(具有相同的目标和意图)
示例
我是 div
策略模式指的是定义一些列的算法,把他们一个个封装起来,目的就是将算法的使用与算法的实现分离开来。
以策略模式的思路实现上边例子的效果的js代码:
1. 首先封装移动节点的缓动算法
1 /* 2 * 缓动算法: 接收4个参数,分别表示: 动画已消失的时间, 小球原始位置, 小球目标位置, 动画持续的总时间 3 */ 4 var tween = { 5 linear: function(t, b, c, d){ 6 return c*t/d + b; 7 }, 8 easeIn: function(t, b, c, d){ 9 return c * ( t /= d ) * t + b; 10 }, 11 strongEaseIn: function(t, b, c, d){ 12 return c * ( t /= d ) * t * t * t * t + b; 13 }, 14 strongEaseOut: function(t, b, c, d){ 15 return c * ( ( t = t / d -1 ) * t * t * t * t + 1 ) + b; 16 }, 17 sineaseIn: function(t, b, c, d){ 18 return c * ( t /= d ) * t * t + b; 19 }, 20 sineaseOut: function(t, b, c, d){ 21 return c * ( ( t = t / d -1 ) * t * t + 1 ) +b; 22 } 23 };
2. HTML 部分
1 <div style="position: relative;border: 1px solid #ff0000; width: 560px;min-height: 30px"> 2 <div style="position:absolute;background:rgba(0,0,255,.3);width: 60px;line-height: 30px;" id="div">我是 div</div> 3 </div> 4 <div> 5 <label><input type="radio" name="tween" value="linear" checked="checked">linear</label> 6 <label><input type="radio" name="tween" value="easeIn">easeIn</label> 7 <label><input type="radio" name="tween" value="strongEaseIn">strongEaseIn</label> 8 <label><input type="radio" name="tween" value="strongEaseOut">strongEaseOut</label> 9 <label><input type="radio" name="tween" value="sineaseIn">sineaseIn</label> 10 <label><input type="radio" name="tween" value="sineaseOut">sineaseOut</label><br/> 11 <input type="button" id="btnRun" value="run"> 12 </div>
3. 定义一个构造函数接收一个参数:即将运动起来的dom节点
1 /* 2 * Animate 的构造函数接收一个参数:即将运动起来的 dom 节点。 3 */ 4 var Animate = function( dom ){ 5 this.dom = dom; // 进行运动的 dom 节点 6 this.startTime = 0; // 动画开始时间 7 this.startPos = 0; // 动画开始时, dom 节点的位置,即 dom 的初始位置 8 this.endPos = 0; // 9 this.propertyName = null; // dom 节点需要被改变的 css 属性名 10 this.easing = null; // 缓动算法 11 this.duration = null; // 动画持续时间 12 };
4. 启动方法,负责启动这个动画
1 /* 2 * 负责启动运动动画 3 */ 4 Animate.prototype.start = function( propertyName, endPos, duration, easing ){ 5 this.startTime = +new Date; // 启动动画的时间 6 this.startPos = this.dom.getBoundingClientRect()[ propertyName ]; // dom 节点的初始位置 7 this.propertyName = propertyName; // dom 节点需要被改变的 CSS 属性名 8 this.endPos = endPos; // dom 节点的目标位置 9 this.duration = duration; // 动画的持续时间 10 this.easing = tween[ easing ]; // 缓动算法 11 12 // 启动动画定时器 13 var self = this; 14 var timeId = setInterval(function(){ 15 if( self.step() === false){ 16 clearInterval(timeId); 17 } 18 },20); 19 };
5. 动画定时器运行的步骤,setp方法,节点运动的每一帧要做的事情
/* * step 表示节点运动的每一帧要做的事情。 负责计算节点的当前位置和调整更新 CSS 属性值 */ Animate.prototype.step = function(){ var t = +new Date; // 取得当前时间 if( t >= this.startTime + this.duration){ // 如果动画结束 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 属性 };
6. 更新节点的CSS属性值
1 /* 2 * 负责更新节点 CSS 属性值 3 */ 4 Animate.prototype.update = function(pos){ 5 this.dom.style[ this.propertyName ] = pos + "px"; 6 };
7. 测试运行
/* * 创建要运动的节点 */ var div = document.getElementById("div"); var animate = new Animate(div); // 设置运行事件 var btnRun = document.getElementById("btnRun"); var radios = document.getElementsByName("tween"); // 获取单选框 btnRun.onclick = function(){ for(var i= 0,lengh=radios.length;i<lengh;i++){ // 查找被选中要执行的算法策略 if(radios[i].checked){ animate.reset(); // 重置节点运动属性 // 设置动画的基本属性 animate.start( "left", 500, 1000, radios[i].value); break; } } };
8. 重置节点的CSS属性为0,reset()方法
Animate.prototype.reset = function(){ // 重置为 0 this.dom.style[this.propertyName] = "0px"; };
策略模式总结:
策略模式的优点:
1. 策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句
2. 策略模式提供了对开放-封闭原则的完美支持,将算法封装在独立的 strategy 中,使得它们易于切换,易于理解,易于扩展
策略模式的缺点:
1. 会在程序中增加许多策略类或者策略对象,但实际上比把他们负责的逻辑堆砌在 Context 中要好
2. 使用策略模式,必须了解所有的策略,才能更好的选择一个合适的策略
阅读参考书籍 - << JavaScript 设计模式与开发实践 >>
缓动算法 - javascript Tween算法及效果