<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>策略模式</title>
</head>
<body>
<div>
<h1>策略模式讲解 和实践</h1>
</div>
<div style="position: relative;border: 1px solid #ff0000; width: 560px;min-height: 30px">
<div style="position:absolute;background:rgba(0,0,255,.3);width: 60px;line-height: 30px;" id="div">我是 div</div>
</div>
<div>
<label><input type="radio" name="tween" value="linear" checked="checked">linear</label>
<label><input type="radio" name="tween" value="easeIn">easeIn</label>
<label><input type="radio" name="tween" value="strongEaseIn">strongEaseIn</label>
<label><input type="radio" name="tween" value="strongEaseOut">strongEaseOut</label>
<label><input type="radio" name="tween" value="sineaseIn">sineaseIn</label>
<label><input type="radio" name="tween" value="sineaseOut">sineaseOut</label><br />
<input type="button" id="btnRun" value="run">
</div>
</body>
</html>
<script>
// 策略模式讲解
// 在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
//在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。
// 意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
// 主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
// 何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
// 如何解决:将这些算法封装成一个一个的类,任意地替换。
// 关键代码:实现同一个接口。
// 应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。 2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 3、JAVA AWT 中的 LayoutManager。
// 优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
// 缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
// 使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
// 2、一个系统需要动态地在几种算法中选择一种。
// 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
// 注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
/*
* 缓动算法: 接收4个参数,分别表示:
* 动画已消失的时间, 小球原始位置, 小球目标位置, 动画持续的总时间
*/
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;
}
}
/*
* Animate 的构造函数接收一个参数:即将运动起来的 dom 节点。
*/
var Animate = function( dom ){
this.dom = dom; // 进行运动的 dom 节点
this.startTime = 0; // 动画开始时间
this.startPos = 0; // 动画开始时, dom 节点的位置,即 dom 的初始位置
this.endPos = 0; //
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 ]; // 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);
}
},20);
};
/*
* 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 属性
};
Animate.prototype.update = function(pos){
this.dom.style[ this.propertyName ] = pos + "px";
};
Animate.prototype.reset = function(){
// 重置为 0
this.dom.style[this.propertyName] = "0px";
};
/*
* 创建要运动的节点
*/
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;
}
}
};
</script>