策略模式的应用

2016-10-12 曹强 更多博文 » 博客 » GitHub »

javascript

原文链接 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;
}