| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 | var SUPPORT_TOUCH = "ontouchstart" in window;var touchEvents = [  ["touchstart", "start"],  ["touchmove", "move"],  ["touchend", "end"],  ["touchcancel", "cancel"]];var mouseEvents = [  ["mousedown", "start"],  ["mouseup", "end"],  ["mousemove", "move"]];var events = SUPPORT_TOUCH ? touchEvents : mouseEvents;function forEach(obj, callback) {  for (key in obj) {    if (obj.hasOwnProperty(key)) callback(obj[key], key, obj);  }}/*   options{    leading:false 表示禁用第一次执行    trailing: false 表示禁用停止触发的回调  }*/function throttle(func, wait, options) {  var timeout, context, args, result;  var previous = 0;  if (!options) options = {};  var later = function() {      previous = options.leading === false ? 0 : new Date().getTime();      timeout = null;      func.apply(context, args);          if (!timeout) context = args = null;  };  var throttled = function() {      var now = new Date().getTime();      if (!previous && options.leading === false) previous = now;      var remaining = wait - (now - previous);      context = this;      args = arguments;      // 如果没有剩余的时间了或者你改了系统时间           if (remaining <= 0 || remaining > wait) {          if (timeout) {              clearTimeout(timeout);              timeout = null;          }          previous = now;          func.apply(context, args);                if (!timeout) context = args = null;      } else if (!timeout && options.trailing !== false) {          timeout = setTimeout(later, remaining);      }  };  return throttled;}/*   @params options : {     string   name    function start ,    function move,     function end,     function cancel  }*/function Gesture(options) {  var _this = this;  forEach(options, function(v, k) {    _this[k] = v;  });  if (this.init) this.init();  events.forEach(function(ev) {    document.addEventListener(      [ev[0]],      function(e) {        if (_this[ev[1]]) _this[ev[1]](e);      },      false    );  });}Gesture.prototype = {  pos: function(e, identifier) {    var target = this.target(e, identifier);    if (target) {      return {        x: target.clientX,        y: target.clientY,        identifier: identifier      };    } else {      return false;    }  },  target: function(e, identifier) {    var target;    if (SUPPORT_TOUCH) {      if (typeof identifier === "number") {        e.changedTouches.forEach(function(touch) {          if (touch.identifier === identifier) target = touch;        });      } else {        target = e.changedTouches[0];      }    } else {      target = e;    }    return target;  },  dispatch: function(e, name, detail) {    e.target.dispatchEvent(      new CustomEvent(name, {        detail: detail,        bubbles: true,        cancelable: true      })    );  }};var tap = new Gesture({  name: "tap",  init: function() {    this.moved = false;    this.startX = 0;    this.startY = 0;    this.number = 0;    this.identifier = undefined;  },  start: function(e) {    if (SUPPORT_TOUCH && e.touches.length > 1) return;    this.init();    var pos = this.pos(e);    this.startX = pos.x;    this.startY = pos.y;    this.identifier = pos.identifier;  },  move: function(e) {    var pos = this.pos(e, this.identifier);    if (      pos &&      (Math.abs(pos.x - this.startX) > 10 || Math.abs(pos.y - this.startY) > 10)    ) {      this.moved = true;    }  },  end: function(e) {    if (this.target(e, this.identifier) && !this.moved) {      this.dispatch(e, this.name, this.pos(e));    }  },  cancel: function(e) {    this.end(e)  }});var pan = new Gesture({  name: "pan",  init: function() {    this.isStart = false;    this.startX = 0;    this.startY = 0;    this.prevX = 0;    this.prevY = 0;    this.prevT = 0;    this.identifier = undefined;    this.history = [];    this.directionX = "none";    this.directionY = "none";  },  start: function(e) {    if (SUPPORT_TOUCH && e.touches.length > 1) return;    this.init();    var pos = this.pos(e);    this.startX = this.prevX = pos.x;    this.startY = this.prevY = pos.y;    this.prevY = e.timeStamp;    this.identifier = pos.identifier;  },  move: function(e) {    var pos = this.pos(e, this.identifier);    if (pos) {      var detail = this.detail(pos);      // 方向相同且时间短记录,不然清空      if (        detail.directionX === this.directionX &&        e.timeStamp - this.prevT < 50      ) {        if (this.history.length >= 10) this.history.shift();      } else {             this.history = [];      }      this.history.push({        x: detail.x,        y: detail.y,        t: e.timeStamp,        deltaX: detail.deltaX,        deltaY: detail.deltaY      });      if (!this.isStart) {        this.isStart = true;        this.dispatch(e, this.name + "start", detail);      }      this.throttleDispatch(e, this.name, detail)      this.prevX = pos.x;      this.prevY = pos.y;      this.prevT = e.timeStamp;      this.directionX = detail.directionX;      this.directionY = detail.directionY;    }  },  end: function(e) {    var pos = this.pos(e, this.identifier);    var detail = this.detail(pos);    detail.fast =      this.history.length > 3 &&      e.timeStamp - this.prevT < 50 &&       Math.abs(        (this.history[this.history.length - 1].x - this.history[0].x) /          (this.history[this.history.length - 1].t - this.history[0].t)      ) > 1;    if (pos && this.isStart) this.dispatch(e, this.name + "end", detail);  },  cancel: function(e) {    this.end(e);  },  detail: function(pos) {    var deltaX = pos.x - this.startX,      deltaY = pos.y - this.startY;    if (this.prevX < pos.x) {      var directionX = "right";    } else if (this.prevX > pos.x) {      var directionX = "left";    } else {      directionX = this.directionX;    }    if (this.prevY < pos.y) {      var directionY = "bottom";    } else if (this.prevY > pos.y) {      var directionY = "top";    } else {      directionY = this.directionY;    }    return {      x: pos.x,      y: pos.y,      deltaX: deltaX,      deltaY: deltaY,      directionY: directionY,      directionX: directionX    };  },  throttleDispatch: throttle(Gesture.prototype.dispatch,20,{ trailing: false })});
 |