0%

策略模式的应用


用策略模式实现缓动动画

需求:让小球以各种缓动效果在页面中运动
动画开始前需要记录的:

  • 小球所在原始位置

  • 小球移动的目标位置

  • 动画开始时的时间点

  • 小球运动的时间点
    之后,用setInterval创建一个定时器,定时器每19秒循环一次。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//缓动算法,此算法移植于flash

var tween = {
//t:动画已消耗的时间,b:小球原始位置,c:小球目标位置,d:动画持续时间
linear: function(t, b, c, d) {
return c*t/d + b;
},
reseIn: 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;
},
sineaseOur: function(t, b, c, d) {
return c * ((t = t / d - 1) * t * t + 1 ) + b;
}
};

接着Animation类

1
2
3
4
5
6
7
8
9
var Animate = function( dom ) {
this.dom = dom; //进行运动的dom节点
this.startTime = 0; //动画开始时间
this.startPos = 0; //动画开始时候dom位置
this.endPos = 0; //结束dom位置
this.propertyName = null; //dom节点需要被改变的属性名
this.easing = null; //缓动算法
this.duration = null; //动画持续时间
};

Animate.prototype.start方法负责启动这个动画
Animate.prototype.step代表小球运动的每一帧要做的事情

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Animate.prototype.start = function( propertyName, endPos, duration, easing) {
this.startTime = +new Date; //动画启动时间
this.startPos = this.dom.getBoundingClientRect()[ propertyName ]; //dom节点初始位置
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);
};

Animate.prototype.update = function(pos) {
this.dom.style[this.propertyName] = pos + 'px';
};

最后,调用

1
2
3
var div = document.getElementById('div');
var animate = new Animate(div);
animate.start('left', 500, 1000, 'strongEaseOut');

表单验证

需求:

  • 用户名不能为空

  • 密码长度不能少于6位

  • 手机号码符合格式

常规,我们可能这么写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var registerForm = document.getElementById('registerForm');

registerForm.onsubmit = function() {
if (registerForm.userName.value === '') {
alert("用户名不能为空");
return false;
}
if (registerForm.password.value.length < 6) {
alert("密码长度不能少于6位");
return false;
}
if (!/(^1[3|5|8][0-9]{9}$)/.test(registerForm.phoneNumber.value)) {
alert("手机号码格式不正确");
return false;
}
}

当然,这个也有函数庞大,缺乏弹性,复用性差等问题,所以,开始突出本文的重点,策略模式的应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
var strategies = {
isNonEmpty: function(value, errorMsg) {
if (value == '') {
return errorMsg;
}
},
minLength: function(value, length, errorMsg) {
if (value.length < length) {
return errorMsg;
}
},
isMobile: function(value, errorMsg) {
if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
return errorMsg;
}
}
};

var Validator = function() {
this.cache = [];
};

Validator.prototype.add = function(dom, rule, errorMsg) {
var ary = rule.split(':');
this.cache.push(function() {
var strategy = ary.shift();
ary.unshift(dom.value);
ary.push(errorMsg);
return strategies[strategy].apply(dom, ary);
})
};

Validator.prototype.start = function() {
for (var i = 0; validataFunc; validataFunc = this.cache[i++]) {
var msg = validataFunc();
if (msg) {
return msg;
}
}
}

var registerForm = document.getElementById('registerForm');
registerForm.onsubmit = function() {
var errorMsg = validataFunc();

if (errorMsg) {
alert(errorMsg);
return false;
}
};

var validataFunc = function() {
var validator = new Validator();

validator.add(registerForm.userName, 'isNonEmpty', '用户名不能为空');
validator.add(registerForm.password, 'minLength:6', '密码长度不能少于6位');
validator.add(registerForm.phoneNumber, 'isMobile', '手机号码格式不正确');

var errorMsg = validator.start();
return errorMsg;
}
我知道是不会有人点的,但万一有人想不开呢?