策略模式的应用
原文链接 https://ronghuaxueleng.github.io/2016/10/12/JavaScript%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%8E%E5%BC%80%E5%8F%91%E5%AE%9E%E8%B7%B5-6%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F%E7%9A%84%E5%BA%94%E7%94%A8/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。
用策略模式实现缓动动画
需求:让小球以各种缓动效果在页面中运动
动画开始前需要记录的:
小球所在原始位置
小球移动的目标位置
动画开始时的时间点
小球运动的时间点
之后,用setInterval
创建一个定时器,定时器每19秒循环一次。//缓动算法,此算法移植于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类
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代表小球运动的每一帧要做的事情
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';
};
最后,调用
var div = document.getElementById('div');
var animate = new Animate(div);
animate.start('left', 500, 1000, 'strongEaseOut');
表单验证
需求:
用户名不能为空
密码长度不能少于6位
手机号码符合格式
常规,我们可能这么写:
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;
}
}
当然,这个也有函数庞大,缺乏弹性,复用性差等问题,所以,开始突出本文的重点,策略模式的应用
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;
}