flipsnap.js 源码阅读笔记

这是官网:http://hokaccha.github.io/js-flipsnap/

1.引入全局命名空间 类似jQuery插件写法   传入window, document,提高内部访问速度;

;(function(window, document, undefined){})(window, window.document)
 

2.定义全局变量

/* 新建div节点 */
var div = document.createElement(‘div‘);
/* 浏览器前缀 */
var prefix = [‘webkit‘, ‘moz‘, ‘o‘, ‘ms‘];
/* 存储属性对象 */
var saveProp = {};
/* 检测浏览器支持的对象 */
var support = Flipsnap.support = {};
/* 手势状态判断 */
var gestureStart = false;
/* 阀值设置 */
var DISTANCE_THRESHOLD = 5;
var ANGLE_THREHOLD = 55;
 

3.分别检测对transform3d, transform及trasition 3个css3属性的支持程度

support.transform3d = hasProp([
  ‘perspectiveProperty‘,
  ‘WebkitPerspective‘,
  ‘MozPerspective‘,
  ‘OPerspective‘,
  ‘msPerspective‘
]);
support.transform = hasProp([
  ‘transformProperty‘,
  ‘WebkitTransform‘,
  ‘MozTransform‘,
  ‘OTransform‘,
  ‘msTransform‘
]);
support.transition = hasProp([
  ‘transitionProperty‘,
  ‘WebkitTransitionProperty‘,
  ‘MozTransitionProperty‘,
  ‘OTransitionProperty‘,
  ‘msTransitionProperty‘
]);

使用hasProp()函数

function hasProp(props) {
  return some(props, function(prop) {
    return div.style[ prop ] !== undefined;
  });
}

其中some()闭包函数##为,只要数组内其中一个为true即判定为true

function some(ary, callback) {
  for (var i = 0, len = ary.length; i < len; i++) {
    if (callback(ary[i], i)) {
      return true;
    }
  }
  return false;
}
 

4.其他浏览器的属性支持(事件监听属性及IE的指针事件的支持)及总的css动画属性支持判断

support.addEventListener = ‘addEventListener‘ in window;
support.mspointer = window.navigator.msPointerEnabled;
support.cssAnimation = (support.transform3d || support.transform) && support.transition;

5.定义事件种类及相关事件对象

var eventTypes = [‘touch‘, ‘mouse‘];
var events = {
  start: {
    touch: ‘touchstart‘,
    mouse: ‘mousedown‘
  },
  move: {
    touch: ‘touchmove‘,
    mouse: ‘mousemove‘
  },
  end: {
    touch: ‘touchend‘,
    mouse: ‘mouseup‘
  }
};

6.添加事件监听

if (support.addEventListener) {
  document.addEventListener(‘gesturestart‘, function() {
    gestureStart = true;
  });
  document.addEventListener(‘gestureend‘, function() {
    gestureStart = false;
  });
}

7.定义全局新类Flipsnap(),并初始化新类,其中使用了Flipsnap类的初始方法init()

function Flipsnap(element, opts) {
  return (this instanceof Flipsnap)
    ? this.init(element, opts)
    : new Flipsnap(element, opts);
}

8.定义Flipsnap类的初始化方法init()

Flipsnap.prototype.init = function(element, opts) {
  var self = this;
  // set element
  self.element = element;
  if (typeof element === ‘string‘) {
    self.element = document.querySelector(element);
  }
  if (!self.element) {
    throw new Error(‘element not found‘);
  }
  if (support.mspointer) {
    self.element.style.msTouchAction = ‘pan-y‘;
  }
  // set opts
  opts = opts || {};
  self.distance = opts.distance;
  self.maxPoint = opts.maxPoint;
  self.disableTouch = (opts.disableTouch === undefined) ? false : opts.disableTouch;
  self.disable3d = (opts.disable3d === undefined) ? false : opts.disable3d;
  self.transitionDuration = (opts.transitionDuration === undefined) ? ‘350ms‘ : opts.transitionDuration + ‘ms‘;
  self.threshold = opts.threshold || 0;
  // set property
  self.currentPoint = 0;
  self.currentX = 0;
  self.animation = false;
  self.use3d = support.transform3d;
  if (self.disable3d === true) {
    self.use3d = false;
  }
  // set default style
  if (support.cssAnimation) {
    self._setStyle({
      transitionProperty: getCSSVal(‘transform‘),
      transitionTimingFunction: ‘cubic-bezier(0,0,0.25,1)‘,
      transitionDuration: ‘0ms‘,
      transform: self._getTranslate(0)
    });
  }
  else {
    self._setStyle({
      position: ‘relative‘,
      left: ‘0px‘
    });
  }
  // initilize
  self.refresh();
  eventTypes.forEach(function(type) {
    // 为什么要传人self作回调函数,(self=Flipsnap())?
    self.element.addEventListener(events.start[type], self, false);
  });
  return self;
};

其中_setStyle()为FLipsnap的内部方法

Flipsnap.prototype._setStyle = function(styles) {
  var self = this;
  var style = self.element.style;
  for (var prop in styles) {
    setStyle(style, prop, styles[prop]);
  }
};

其中setStyle()函数,设置对应css属性为对应值,具体为

function setStyle(style, prop, val) {
  var _saveProp = saveProp[ prop ];
  if (_saveProp) {
    style[ _saveProp ] = val;
  }
  else if (style[ prop ] !== undefined) {
    saveProp[ prop ] = prop;
    style[ prop ] = val;
  }
  else {
    some(prefix, function(_prefix) {
      var _prop = ucFirst(_prefix) + ucFirst(prop);
      if (style[ _prop ] !== undefined) {
        saveProp[ prop ] = _prop;
        style[ _prop ] = val;
        return true;
      }
    });
  }
}

并在saveProp中设置过的属性

getCSSVal()函数用于获取已用的css属性值,具体如下

function getCSSVal(prop) {
  if (div.style[ prop ] !== undefined) {
    return prop;
  }
  else {
    var ret;
    some(prefix, function(_prefix) {
      var _prop = ucFirst(_prefix) + ucFirst(prop);
      if (div.style[ _prop ] !== undefined) {
        ret = ‘-‘ + _prefix + ‘-‘ + prop;
        return true;
      }
    });
    return ret;
  }
}

ucFirst()函数用于将首字母变大写,具体如下 截取第一个字符串charAt(0)转为大写 拼接后面剩下的字符串(substr(1))

function ucFirst(str) {
  return str.charAt(0).toUpperCase() + str.substr(1);
}
 

self._getTranslate为Flipsnap的内部方法

Flipsnap.prototype._getTranslate = function(x) {
  var self = this;
  return self.use3d
    ? ‘translate3d(‘ + x + ‘px, 0, 0)‘
    : ‘translate(‘ + x + ‘px, 0)‘;
};

refresh()的Flipsnap方法,具体如下

Flipsnap.prototype.refresh = function() {
  var self = this;
  // setting max point
  self._maxPoint = (self.maxPoint === undefined) ? (function() {
    var childNodes = self.element.childNodes,
      itemLength = -1,
      i = 0,
      len = childNodes.length,
      node;
    for(; i < len; i++) {
      node = childNodes[i];
      if (node.nodeType === 1) {
        itemLength++;
      }
    }
    return itemLength;
  })() : self.maxPoint;
  // setting distance
  if (self.distance === undefined) {
    if (self._maxPoint < 0) {
      self._distance = 0;
    }
    else {
      self._distance = self.element.scrollWidth / (self._maxPoint + 1);
    }
  }
  else {
    self._distance = self.distance;
  }
  // setting maxX
  self._maxX = -self._distance * self._maxPoint;
  self.moveToPoint();
};

通过refresh方法设定_maxPoint(最多移动次数)、_distance(移动距离)和_maxX(最大x轴偏向值)属性,从而控制最多的滑动次数;

moveToPoint()的Flipsnap方法,判定是否需要滑动并触发事件,具体如下

Flipsnap.prototype.moveToPoint = function(point, transitionDuration) {
  var self = this;
  transitionDuration = transitionDuration === undefined
    ? self.transitionDuration : transitionDuration + ‘ms‘;
  var beforePoint = self.currentPoint;
  // not called from `refresh()`
  if (point === undefined) {
    point = self.currentPoint;
  }
  if (point < 0) {
    self.currentPoint = 0;
  }
  else if (point > self._maxPoint) {
    self.currentPoint = self._maxPoint;
  }
  else {
    self.currentPoint = parseInt(point, 10);
  }
  if (support.cssAnimation) {
    self._setStyle({ transitionDuration: transitionDuration });
  }
  else {
    self.animation = true;
  }
  self._setX(- self.currentPoint * self._distance, transitionDuration);
  if (beforePoint !== self.currentPoint) { // is move?
    // `fsmoveend` is deprecated
    // `fspointmove` is recommend.
    self._triggerEvent(‘fsmoveend‘, true, false);
    self._triggerEvent(‘fspointmove‘, true, false);
  }
};

其中_setX()为Flipsnap的内部方法,具体如下

Flipsnap.prototype._setX = function(x, transitionDuration) {
  var self = this;
  self.currentX = x;
  if (support.cssAnimation) {
    self.element.style[ saveProp.transform ] = self._getTranslate(x);
  }
  else {
    if (self.animation) {
      self._animate(x, transitionDuration || self.transitionDuration);
    }
    else {
      self.element.style.left = x + ‘px‘;
    }
  }
};

其中_animate()为Flipsnap的内部方法,具体如下

Flipsnap.prototype._animate = function(x, transitionDuration) {
  var self = this;
  var elem = self.element;
  var begin = +new Date();
  var from = parseInt(elem.style.left, 10);
  var to = x;
  var duration = parseInt(transitionDuration, 10);
  var easing = function(time, duration) {
    return -(time /= duration) * (time - 2);
  };
  var timer = setInterval(function() {
    var time = new Date() - begin;
    var pos, now;
    if (time > duration) {
      clearInterval(timer);
      now = to;
    }
    else {
      pos = easing(time, duration);
      now = pos * (to - from) + from;
    }
    elem.style.left = now + "px";
  }, 10);
};

其中_triggerEvent() 为Flipsnap的内部方法,具体如下

Flipsnap.prototype._triggerEvent = function(type, bubbles, cancelable, data) {
  var self = this;
  var ev = document.createEvent(‘Event‘);
  ev.initEvent(type, bubbles, cancelable);
  if (data) {
    for (var d in data) {
      if (data.hasOwnProperty(d)) {
        ev[d] = data[d];
      }
    }
  }
  return self.element.dispatchEvent(ev);
};

9.定义Flipsnap类的事件控制方法handleEvent()

Flipsnap.prototype.handleEvent = function(event) {
  var self = this;
  switch (event.type) {
    // start
    case events.start.touch: self._touchStart(event, ‘touch‘); break;
    case events.start.mouse: self._touchStart(event, ‘mouse‘); break;
    // move
    case events.move.touch: self._touchMove(event, ‘touch‘); break;
    case events.move.mouse: self._touchMove(event, ‘mouse‘); break;
    // end
    case events.end.touch: self._touchEnd(event, ‘touch‘); break;
    case events.end.mouse: self._touchEnd(event, ‘mouse‘); break;
    // click
    case ‘click‘: self._click(event); break;
  }
};

通过event.type进行条件判断,确定执行Flipsnap的四个内部处理方法中的一个

_touchStart()方法

Flipsnap.prototype._touchStart = function(event, type) {
  var self = this;
  if (self.disableTouch || self.scrolling || gestureStart) {
    return;
  }
  self.element.addEventListener(events.move[type], self, false);
  document.addEventListener(events.end[type], self, false);
  var tagName = event.target.tagName;
  if (type === ‘mouse‘ && tagName !== ‘SELECT‘ && tagName !== ‘INPUT‘ && tagName !== ‘TEXTAREA‘ && tagName !== ‘BUTTON‘) {
    event.preventDefault();
  }
  if (support.cssAnimation) {
    self._setStyle({ transitionDuration: ‘0ms‘ });
  }
  else {
    self.animation = false;
  }
  self.scrolling = true;
  self.moveReady = false;
  self.startPageX = getPage(event, ‘pageX‘);
  self.startPageY = getPage(event, ‘pageY‘);
  self.basePageX = self.startPageX;
  self.directionX = 0;
  self.startTime = event.timeStamp;
  self._triggerEvent(‘fstouchstart‘, true, false);
};

通过touchStart方法记录下触摸开始点开始时间等参数,并触发fstouchstart事件;

getPage()函数如下

function getPage(event, page) {
  return event.changedTouches ? event.changedTouches[0][page] : event[page];
}

_touchMove()方法如下

Flipsnap.prototype._touchMove = function(event, type) {
  var self = this;
  if (!self.scrolling || gestureStart) {
    return;
  }
  var pageX = getPage(event, ‘pageX‘);
  var pageY = getPage(event, ‘pageY‘);
  var distX;
  var newX;
  if (self.moveReady) {
    event.preventDefault();
    distX = pageX - self.basePageX;
    newX = self.currentX + distX;
    if (newX >= 0 || newX < self._maxX) {
      newX = Math.round(self.currentX + distX / 3);
    }
    // When distX is 0, use one previous value.
    // For android firefox. When touchend fired, touchmove also
    // fired and distX is certainly set to 0.
    self.directionX =
      distX === 0 ? self.directionX :
      distX > 0 ? -1 : 1;
    // if they prevent us then stop it
    var isPrevent = !self._triggerEvent(‘fstouchmove‘, true, true, {
      delta: distX,
      direction: self.directionX
    });
    if (isPrevent) {
      self._touchAfter({
        moved: false,
        originalPoint: self.currentPoint,
        newPoint: self.currentPoint,
        cancelled: true
      });
    } else {
      self._setX(newX);
    }
  }
  else {
    // https://github.com/pxgrid/js-flipsnap/pull/36
    var triangle = getTriangleSide(self.startPageX, self.startPageY, pageX, pageY);
    if (triangle.z > DISTANCE_THRESHOLD) {
      if (getAngle(triangle) > ANGLE_THREHOLD) {
        event.preventDefault();
        self.moveReady = true;
        self.element.addEventListener(‘click‘, self, true);
      }
      else {
        self.scrolling = false;
      }
    }
  }
  self.basePageX = pageX;
};

其中getTriangleSide()函数如下

function getTriangleSide(x1, y1, x2, y2) {
  var x = Math.abs(x1 - x2);
  var y = Math.abs(y1 - y2);
  var z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
  return {
    x: x,
    y: y,
    z: z
  };
}

其中getAngle()函数如下

function getAngle(triangle) {
  var cos = triangle.y / triangle.z;
  var radina = Math.acos(cos);
  return 180 / (Math.PI / radina);
}

_touchEnd()方法如下

Flipsnap.prototype._touchEnd = function(event, type) {
  var self = this;
  self.element.removeEventListener(events.move[type], self, false);
  document.removeEventListener(events.end[type], self, false);
  if (!self.scrolling) {
    return;
  }
  var newPoint = -self.currentX / self._distance;
  newPoint =
    (self.directionX > 0) ? Math.ceil(newPoint) :
    (self.directionX < 0) ? Math.floor(newPoint) :
    Math.round(newPoint);
  if (newPoint < 0) {
    newPoint = 0;
  }
  else if (newPoint > self._maxPoint) {
    newPoint = self._maxPoint;
  }
  if (Math.abs(self.startPageX - self.basePageX) < self.threshold) {
    newPoint = self.currentPoint;
  }
  self._touchAfter({
    moved: newPoint !== self.currentPoint,
    originalPoint: self.currentPoint,
    newPoint: newPoint,
    cancelled: false
  });
  self.moveToPoint(newPoint);
};

触发fstouchend事件

其中_touchAfter()方法如下

Flipsnap.prototype._touchAfter = function(params) {
  var self = this;
  self.scrolling = false;
  self.moveReady = false;
  setTimeout(function() {
    self.element.removeEventListener(‘click‘, self, true);
  }, 200);
  self._triggerEvent(‘fstouchend‘, true, false, params);
};

_clic()方法如下

Flipsnap.prototype._click = function(event) {
  var self = this;
  event.stopPropagation();
  event.preventDefault();
};

10.定义Flipsnap类的状态判断方法hasNext()

Flipsnap.prototype.hasNext = function() {
  var self = this;
  return self.currentPoint < self._maxPoint;
};

11.定义Flipsnap类的状态判断方法hasPrev()

Flipsnap.prototype.hasPrev = function() {
  var self = this;
  return self.currentPoint > 0;
};

12.定义Flipsnap类的跳转下一个方法toNext()

Flipsnap.prototype.toNext = function(transitionDuration) {
  var self = this;
  if (!self.hasNext()) {
    return;
  }
  self.moveToPoint(self.currentPoint + 1, transitionDuration);
};

13.定义Flipsnap类的跳转上一个方法toPrev()

Flipsnap.prototype.toPrev = function(transitionDuration) {
  var self = this;
  if (!self.hasPrev()) {
    return;
  }
  self.moveToPoint(self.currentPoint - 1, transitionDuration);
};

14.定义Flipsnap类的跳转上一个方法destroy()

Flipsnap.prototype.destroy = function() {
  var self = this;
  eventTypes.forEach(function(type) {
    self.element.removeEventListener(events.start[type], self, false);
  });
};

取消在各个节点上的事件监听

15.将Flipsnap模块化

if (typeof exports == ‘object‘) {
  module.exports = Flipsnap;
}
else if (typeof define == ‘function‘ && define.amd) {
  define(function() {
    return Flipsnap;
  });
}
else {
  window.Flipsnap = Flipsnap;
}
/**
 * flipsnap.js
 *
 * @version  0.6.2
 * @url http://hokaccha.github.com/js-flipsnap/
 *
 * Copyright 2011 PixelGrid, Inc.
 * Licensed under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 */

(function(window, document, undefined) {

var div = document.createElement(‘div‘);
var prefix = [‘webkit‘, ‘moz‘, ‘o‘, ‘ms‘];
var saveProp = {};
var support = Flipsnap.support = {};
var gestureStart = false;

var DISTANCE_THRESHOLD = 5;
var ANGLE_THREHOLD = 55;

support.transform3d = hasProp([
  ‘perspectiveProperty‘,
  ‘WebkitPerspective‘,
  ‘MozPerspective‘,
  ‘OPerspective‘,
  ‘msPerspective‘
]);

support.transform = hasProp([
  ‘transformProperty‘,
  ‘WebkitTransform‘,
  ‘MozTransform‘,
  ‘OTransform‘,
  ‘msTransform‘
]);

support.transition = hasProp([
  ‘transitionProperty‘,
  ‘WebkitTransitionProperty‘,
  ‘MozTransitionProperty‘,
  ‘OTransitionProperty‘,
  ‘msTransitionProperty‘
]);

support.addEventListener = ‘addEventListener‘ in window;
support.mspointer = window.navigator.msPointerEnabled;

support.cssAnimation = (support.transform3d || support.transform) && support.transition;

var eventTypes = [‘touch‘, ‘mouse‘];
var events = {
  start: {
    touch: ‘touchstart‘,
    mouse: ‘mousedown‘
  },
  move: {
    touch: ‘touchmove‘,
    mouse: ‘mousemove‘
  },
  end: {
    touch: ‘touchend‘,
    mouse: ‘mouseup‘
  }
};

if (support.addEventListener) {
  document.addEventListener(‘gesturestart‘, function() {
    gestureStart = true;
  });

  document.addEventListener(‘gestureend‘, function() {
    gestureStart = false;
  });
}

function Flipsnap(element, opts) {
  return (this instanceof Flipsnap)
    ? this.init(element, opts)
    : new Flipsnap(element, opts);
}

Flipsnap.prototype.init = function(element, opts) {
  var self = this;

  // set element
  self.element = element;
  if (typeof element === ‘string‘) {
    self.element = document.querySelector(element);
  }

  if (!self.element) {
    throw new Error(‘element not found‘);
  }

  if (support.mspointer) {
    self.element.style.msTouchAction = ‘pan-y‘;
  }

  // set opts
  opts = opts || {};
  self.distance = opts.distance;
  self.maxPoint = opts.maxPoint;
  self.disableTouch = (opts.disableTouch === undefined) ? false : opts.disableTouch;
  self.disable3d = (opts.disable3d === undefined) ? false : opts.disable3d;
  self.transitionDuration = (opts.transitionDuration === undefined) ? ‘350ms‘ : opts.transitionDuration + ‘ms‘;

  // set property
  self.currentPoint = 0;
  self.currentX = 0;
  self.animation = false;
  self.use3d = support.transform3d;
  if (self.disable3d === true) {
    self.use3d = false;
  }

  // set default style
  if (support.cssAnimation) {
    self._setStyle({
      transitionProperty: getCSSVal(‘transform‘),
      transitionTimingFunction: ‘cubic-bezier(0,0,0.25,1)‘,
      transitionDuration: ‘0ms‘,
      transform: self._getTranslate(0)
    });
  }
  else {
    self._setStyle({
      position: ‘relative‘,
      left: ‘0px‘
    });
  }

  // initilize
  self.refresh();

  eventTypes.forEach(function(type) {
    self.element.addEventListener(events.start[type], self, false);
  });

  return self;
};

Flipsnap.prototype.handleEvent = function(event) {
  var self = this;

  switch (event.type) {
    // start
    case events.start.touch: self._touchStart(event, ‘touch‘); break;
    case events.start.mouse: self._touchStart(event, ‘mouse‘); break;

    // move
    case events.move.touch: self._touchMove(event, ‘touch‘); break;
    case events.move.mouse: self._touchMove(event, ‘mouse‘); break;

    // end
    case events.end.touch: self._touchEnd(event, ‘touch‘); break;
    case events.end.mouse: self._touchEnd(event, ‘mouse‘); break;

    // click
    case ‘click‘: self._click(event); break;
  }
};

Flipsnap.prototype.refresh = function() {
  var self = this;

  // setting max point
  self._maxPoint = (self.maxPoint === undefined) ? (function() {
    var childNodes = self.element.childNodes,
      itemLength = -1,
      i = 0,
      len = childNodes.length,
      node;
    for(; i < len; i++) {
      node = childNodes[i];
      if (node.nodeType === 1) {
        itemLength++;
      }
    }

    return itemLength;
  })() : self.maxPoint;

  // setting distance
  if (self.distance === undefined) {
    if (self._maxPoint < 0) {
      self._distance = 0;
    }
    else {
      self._distance = self.element.scrollWidth / (self._maxPoint + 1);
    }
  }
  else {
    self._distance = self.distance;
  }

  // setting maxX
  self._maxX = -self._distance * self._maxPoint;

  self.moveToPoint();
};

Flipsnap.prototype.hasNext = function() {
  var self = this;

  return self.currentPoint < self._maxPoint;
};

Flipsnap.prototype.hasPrev = function() {
  var self = this;

  return self.currentPoint > 0;
};

Flipsnap.prototype.toNext = function(transitionDuration) {
  var self = this;

  if (!self.hasNext()) {
    return;
  }

  self.moveToPoint(self.currentPoint + 1, transitionDuration);
};

Flipsnap.prototype.toPrev = function(transitionDuration) {
  var self = this;

  if (!self.hasPrev()) {
    return;
  }

  self.moveToPoint(self.currentPoint - 1, transitionDuration);
};

Flipsnap.prototype.moveToPoint = function(point, transitionDuration) {
  var self = this;

  transitionDuration = transitionDuration === undefined
    ? self.transitionDuration : transitionDuration + ‘ms‘;

  var beforePoint = self.currentPoint;

  // not called from `refresh()`
  if (point === undefined) {
    point = self.currentPoint;
  }

  if (point < 0) {
    self.currentPoint = 0;
  }
  else if (point > self._maxPoint) {
    self.currentPoint = self._maxPoint;
  }
  else {
    self.currentPoint = parseInt(point, 10);
  }

  if (support.cssAnimation) {
    self._setStyle({ transitionDuration: transitionDuration });
  }
  else {
    self.animation = true;
  }
  self._setX(- self.currentPoint * self._distance, transitionDuration);

  if (beforePoint !== self.currentPoint) { // is move?
    // `fsmoveend` is deprecated
    // `fspointmove` is recommend.
    self._triggerEvent(‘fsmoveend‘, true, false);
    self._triggerEvent(‘fspointmove‘, true, false);
  }
};

Flipsnap.prototype._setX = function(x, transitionDuration) {
  var self = this;

  self.currentX = x;
  if (support.cssAnimation) {
    self.element.style[ saveProp.transform ] = self._getTranslate(x);
  }
  else {
    if (self.animation) {
      self._animate(x, transitionDuration || self.transitionDuration);
    }
    else {
      self.element.style.left = x + ‘px‘;
    }
  }
};

Flipsnap.prototype._touchStart = function(event, type) {
  var self = this;

  if (self.disableTouch || self.scrolling || gestureStart) {
    return;
  }

  self.element.addEventListener(events.move[type], self, false);
  document.addEventListener(events.end[type], self, false);

  var tagName = event.target.tagName;
  if (type === ‘mouse‘ && tagName !== ‘SELECT‘ && tagName !== ‘INPUT‘ && tagName !== ‘TEXTAREA‘ && tagName !== ‘BUTTON‘) {
    event.preventDefault();
  }

  if (support.cssAnimation) {
    self._setStyle({ transitionDuration: ‘0ms‘ });
  }
  else {
    self.animation = false;
  }
  self.scrolling = true;
  self.moveReady = false;
  self.startPageX = getPage(event, ‘pageX‘);
  self.startPageY = getPage(event, ‘pageY‘);
  self.basePageX = self.startPageX;
  self.directionX = 0;
  self.startTime = event.timeStamp;
  self._triggerEvent(‘fstouchstart‘, true, false);
};

Flipsnap.prototype._touchMove = function(event, type) {
  var self = this;

  if (!self.scrolling || gestureStart) {
    return;
  }

  var pageX = getPage(event, ‘pageX‘);
  var pageY = getPage(event, ‘pageY‘);
  var distX;
  var newX;

  if (self.moveReady) {
    event.preventDefault();

    distX = pageX - self.basePageX;
    newX = self.currentX + distX;
    if (newX >= 0 || newX < self._maxX) {
      newX = Math.round(self.currentX + distX / 3);
    }

    // When distX is 0, use one previous value.
    // For android firefox. When touchend fired, touchmove also
    // fired and distX is certainly set to 0.
    self.directionX =
      distX === 0 ? self.directionX :
      distX > 0 ? -1 : 1;

    // if they prevent us then stop it
    var isPrevent = !self._triggerEvent(‘fstouchmove‘, true, true, {
      delta: distX,
      direction: self.directionX
    });

    if (isPrevent) {
      self._touchAfter({
        moved: false,
        originalPoint: self.currentPoint,
        newPoint: self.currentPoint,
        cancelled: true
      });
    } else {
      self._setX(newX);
    }
  }
  else {
    // https://github.com/hokaccha/js-flipsnap/pull/36
    var triangle = getTriangleSide(self.startPageX, self.startPageY, pageX, pageY);
    if (triangle.z > DISTANCE_THRESHOLD) {
      if (getAngle(triangle) > ANGLE_THREHOLD) {
        event.preventDefault();
        self.moveReady = true;
        self.element.addEventListener(‘click‘, self, true);
      }
      else {
        self.scrolling = false;
      }
    }
  }

  self.basePageX = pageX;
};

Flipsnap.prototype._touchEnd = function(event, type) {
  var self = this;

  self.element.removeEventListener(events.move[type], self, false);
  document.removeEventListener(events.end[type], self, false);

  if (!self.scrolling) {
    return;
  }

  var newPoint = -self.currentX / self._distance;
  newPoint =
    (self.directionX > 0) ? Math.ceil(newPoint) :
    (self.directionX < 0) ? Math.floor(newPoint) :
    Math.round(newPoint);

  if (newPoint < 0) {
    newPoint = 0;
  }
  else if (newPoint > self._maxPoint) {
    newPoint = self._maxPoint;
  }

  self._touchAfter({
    moved: newPoint !== self.currentPoint,
    originalPoint: self.currentPoint,
    newPoint: newPoint,
    cancelled: false
  });

  self.moveToPoint(newPoint);
};

Flipsnap.prototype._click = function(event) {
  var self = this;

  event.stopPropagation();
  event.preventDefault();
};

Flipsnap.prototype._touchAfter = function(params) {
  var self = this;

  self.scrolling = false;
  self.moveReady = false;

  setTimeout(function() {
    self.element.removeEventListener(‘click‘, self, true);
  }, 200);

  self._triggerEvent(‘fstouchend‘, true, false, params);
};

Flipsnap.prototype._setStyle = function(styles) {
  var self = this;
  var style = self.element.style;

  for (var prop in styles) {
    setStyle(style, prop, styles[prop]);
  }
};

Flipsnap.prototype._animate = function(x, transitionDuration) {
  var self = this;

  var elem = self.element;
  var begin = +new Date();
  var from = parseInt(elem.style.left, 10);
  var to = x;
  var duration = parseInt(transitionDuration, 10);
  var easing = function(time, duration) {
    return -(time /= duration) * (time - 2);
  };
  var timer = setInterval(function() {
    var time = new Date() - begin;
    var pos, now;
    if (time > duration) {
      clearInterval(timer);
      now = to;
    }
    else {
      pos = easing(time, duration);
      now = pos * (to - from) + from;
    }
    elem.style.left = now + "px";
  }, 10);
};

Flipsnap.prototype.destroy = function() {
  var self = this;

  eventTypes.forEach(function(type) {
    self.element.removeEventListener(events.start[type], self, false);
  });
};

Flipsnap.prototype._getTranslate = function(x) {
  var self = this;

  return self.use3d
    ? ‘translate3d(‘ + x + ‘px, 0, 0)‘
    : ‘translate(‘ + x + ‘px, 0)‘;
};

Flipsnap.prototype._triggerEvent = function(type, bubbles, cancelable, data) {
  var self = this;

  var ev = document.createEvent(‘Event‘);
  ev.initEvent(type, bubbles, cancelable);

  if (data) {
    for (var d in data) {
      if (data.hasOwnProperty(d)) {
        ev[d] = data[d];
      }
    }
  }

  return self.element.dispatchEvent(ev);
};

function getPage(event, page) {
  return event.changedTouches ? event.changedTouches[0][page] : event[page];
}

function hasProp(props) {
  return some(props, function(prop) {
    return div.style[ prop ] !== undefined;
  });
}

function setStyle(style, prop, val) {
  var _saveProp = saveProp[ prop ];
  if (_saveProp) {
    style[ _saveProp ] = val;
  }
  else if (style[ prop ] !== undefined) {
    saveProp[ prop ] = prop;
    style[ prop ] = val;
  }
  else {
    some(prefix, function(_prefix) {
      var _prop = ucFirst(_prefix) + ucFirst(prop);
      if (style[ _prop ] !== undefined) {
        saveProp[ prop ] = _prop;
        style[ _prop ] = val;
        return true;
      }
    });
  }
}

function getCSSVal(prop) {
  if (div.style[ prop ] !== undefined) {
    return prop;
  }
  else {
    var ret;
    some(prefix, function(_prefix) {
      var _prop = ucFirst(_prefix) + ucFirst(prop);
      if (div.style[ _prop ] !== undefined) {
        ret = ‘-‘ + _prefix + ‘-‘ + prop;
        return true;
      }
    });
    return ret;
  }
}

function ucFirst(str) {
  return str.charAt(0).toUpperCase() + str.substr(1);
}

function some(ary, callback) {
  for (var i = 0, len = ary.length; i < len; i++) {
    if (callback(ary[i], i)) {
      return true;
    }
  }
  return false;
}

function getTriangleSide(x1, y1, x2, y2) {
  var x = Math.abs(x1 - x2);
  var y = Math.abs(y1 - y2);
  var z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));

  return {
    x: x,
    y: y,
    z: z
  };
}

function getAngle(triangle) {
  var cos = triangle.y / triangle.z;
  var radina = Math.acos(cos);

  return 180 / (Math.PI / radina);
}

if (typeof exports == ‘object‘) {
  module.exports = Flipsnap;
}
else if (typeof define == ‘function‘ && define.amd) {
  define(function() {
    return Flipsnap;
  });
}
else {
  window.Flipsnap = Flipsnap;
}

})(window, window.document);

  

时间: 2024-10-20 17:14:08

flipsnap.js 源码阅读笔记的相关文章

IOS测试框架之:athrun的InstrumentDriver源码阅读笔记

athrun的InstrumentDriver源码阅读笔记 作者:唯一 athrun是淘宝的开源测试项目,InstrumentDriver是ios端的实现,之前在公司项目中用过这个框架,没有深入了解,现在回来记录下. 官方介绍:http://code.taobao.org/p/athrun/wiki/instrumentDriver/ 优点:这个框架是对UIAutomation的java实现,在代码提示.用例维护方面比UIAutomation强多了,借junit4的光,我们可以通过junit4的

Yii源码阅读笔记 - 日志组件

?使用 Yii框架为开发者提供两个静态方法进行日志记录: Yii::log($message, $level, $category);Yii::trace($message, $category); 两者的区别在于后者依赖于应用开启调试模式,即定义常量YII_DEBUG: defined('YII_DEBUG') or define('YII_DEBUG', true); Yii::log方法的调用需要指定message的level和category.category是格式为“xxx.yyy.z

CI框架源码阅读笔记3 全局函数Common.php

从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap引导文件都会最先引入全局函数,以便于之后的处理工作). 打开Common.php中,第一行代码就非常诡异: if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 上一篇(CI框架源码阅读笔记2 一切的入口 index

源码阅读笔记 - 1 MSVC2015中的std::sort

大约寒假开始的时候我就已经把std::sort的源码阅读完毕并理解其中的做法了,到了寒假结尾,姑且把它写出来 这是我的第一篇源码阅读笔记,以后会发更多的,包括算法和库实现,源码会按照我自己的代码风格格式化,去掉或者展开用于条件编译或者debug检查的宏,依重要程度重新排序函数,但是不会改变命名方式(虽然MSVC的STL命名实在是我不能接受的那种),对于代码块的解释会在代码块前(上面)用注释标明. template<class _RanIt, class _Diff, class _Pr> in

CI框架源码阅读笔记5 基准测试 BenchMark.php

上一篇博客(CI框架源码阅读笔记4 引导文件CodeIgniter.php)中,我们已经看到:CI中核心流程的核心功能都是由不同的组件来完成的.这些组件类似于一个一个单独的模块,不同的模块完成不同的功能,各模块之间可以相互调用,共同构成了CI的核心骨架. 从本篇开始,将进一步去分析各组件的实现细节,深入CI核心的黑盒内部(研究之后,其实就应该是白盒了,仅仅对于应用来说,它应该算是黑盒),从而更好的去认识.把握这个框架. 按照惯例,在开始之前,我们贴上CI中不完全的核心组件图: 由于BenchMa

CI框架源码阅读笔记2 一切的入口 index.php

上一节(CI框架源码阅读笔记1 - 环境准备.基本术语和框架流程)中,我们提到了CI框架的基本流程,这里这次贴出流程图,以备参考: 作为CI框架的入口文件,源码阅读,自然由此开始.在源码阅读的过程中,我们并不会逐行进行解释,而只解释核心的功能和实现. 1.       设置应用程序环境 define('ENVIRONMENT', 'development'); 这里的development可以是任何你喜欢的环境名称(比如dev,再如test),相对应的,你要在下面的switch case代码块中

Apache Storm源码阅读笔记

欢迎转载,转载请注明出处. 楔子 自从建了Spark交流的QQ群之后,热情加入的同学不少,大家不仅对Spark很热衷对于Storm也是充满好奇.大家都提到一个问题就是有关storm内部实现机理的资料比较少,理解起来非常费劲. 尽管自己也陆续对storm的源码走读发表了一些博文,当时写的时候比较匆忙,有时候衔接的不是太好,此番做了一些整理,主要是针对TridentTopology部分,修改过的内容采用pdf格式发布,方便打印. 文章中有些内容的理解得益于徐明明和fxjwind两位的指点,非常感谢.

CI框架源码阅读笔记4 引导文件CodeIgniter.php

到了这里,终于进入CI框架的核心了.既然是"引导"文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http://you.host.com/usr/reg 经过引导文件,实际上会交给Application中的UsrController控制器的reg方法去处理. 这之中,CodeIgniter.php做了哪些工作?我们一步步来看. 1.    导入预定义常量.框架环境初始化 之前的一篇博客(CI框架源码阅读笔记2 一切的入

分享:json2.js源码解读笔记

1. 如何理解"json" 首先应该意识到,json是一种数据转换格式,既然是个"格式",就是个抽象的东西.它不是js对象,也不是字符串,它只是一种格式,一种规定而已. 这个格式规定了如何将js对象转换成字符串.以及转换成怎样的字符串--序列化 -- JSON.stringify 接口: 以及如何将一个有效字符串转换成js对象--反序列化-- JSON.parse 接口: 2. 关于作者 json作者是 道格拉斯.克劳福德 ,是一位js大牛,写过一本<java