@kirkzwy
2014-11-27T02:53:17.000000Z
字数 11802
阅读 3320
coding flipsnap.js
(function(window, document, undefined){})(window, window.document)
/* 新建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;
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;}
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;});}
类Flipsnap(),并初始化新类,其中使用了Flipsnap类的初始方法init()
function Flipsnap(element, opts) {return (this instanceof Flipsnap)? this.init(element, opts): new Flipsnap(element, opts);}
init()
Flipsnap.prototype.init = function(element, opts) {var self = this;// set elementself.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 optsopts = 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 propertyself.currentPoint = 0;self.currentX = 0;self.animation = false;self.use3d = support.transform3d;if (self.disable3d === true) {self.use3d = false;}// set default styleif (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'});}// initilizeself.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()函数用于将首字母变大写,具体如下
function ucFirst(str) {return str.charAt(0).toUpperCase() + str.substr(1);}
cubic-bezier(0,0,0.25,1)为css3的变形值
self._getTranslate为Flipsnap的内部方法定义3d属性值,具体为
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 pointself._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 distanceif (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 maxXself._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);}};
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._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._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);};
Flipsnap.prototype.handleEvent = function(event) {var self = this;switch (event.type) {// startcase events.start.touch: self._touchStart(event, 'touch'); break;case events.start.mouse: self._touchStart(event, 'mouse'); break;// movecase events.move.touch: self._touchMove(event, 'touch'); break;case events.move.mouse: self._touchMove(event, 'mouse'); break;// endcase events.end.touch: self._touchEnd(event, 'touch'); break;case events.end.mouse: self._touchEnd(event, 'mouse'); break;// clickcase 'click': self._click(event); break;}};
通过event.type进行条件判断,确定执行Flipsnap的四个内部处理方法中的一个
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事件;
function getPage(event, page) {return event.changedTouches ? event.changedTouches[0][page] : event[page];}
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 itvar 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/36var 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;};
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);}
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事件
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._click = function(event) {var self = this;event.stopPropagation();event.preventDefault();};
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.destroy = function() {var self = this;eventTypes.forEach(function(type) {self.element.removeEventListener(events.start[type], self, false);});};
取消在各个节点上的事件监听
if (typeof exports == 'object') {module.exports = Flipsnap;}else if (typeof define == 'function' && define.amd) {define(function() {return Flipsnap;});}else {window.Flipsnap = Flipsnap;}