首页 > 代码库 > iscroll源码学习(1)

iscroll源码学习(1)

iscroll是移端端开发的两大利器之一(另一个是fastclick),为了将它整合的avalon,需要对它认真学习一番。下面是我的笔记。

第一天看的是它的工具类util.js

//用于做函数节流var rAF = window.requestAnimationFrame	||	window.webkitRequestAnimationFrame	||	window.mozRequestAnimationFrame		||	window.oRequestAnimationFrame		||	window.msRequestAnimationFrame		||	function (callback) { window.setTimeout(callback, 1000 / 60); };var utils = (function () {	var me = {};	var _elementStyle = document.createElement(‘div‘).style;        //取得可以用的私有前缀	var _vendor = (function () {		var vendors = [‘t‘, ‘webkitT‘, ‘MozT‘, ‘msT‘, ‘OT‘],			transform,			i = 0,			l = vendors.length;		for ( ; i < l; i++ ) {			transform = vendors[i] + ‘ransform‘;			if ( transform in _elementStyle ) return vendors[i].substr(0, vendors[i].length-1);		}		return false;	})();        //根据私有前缀修正属性名	function _prefixStyle (style) {		if ( _vendor === false ) return false;		if ( _vendor === ‘‘ ) return style;		return _vendor + style.charAt(0).toUpperCase() + style.substr(1);	}        //取得当前时间截	me.getTime = Date.now || function getTime () { return new Date().getTime(); };        //糅杂	me.extend = function (target, obj) {		for ( var i in obj ) {			target[i] = obj[i];		}	};        //添加事件	me.addEvent = function (el, type, fn, capture) {		el.addEventListener(type, fn, !!capture);	};        //卸载事件	me.removeEvent = function (el, type, fn, capture) {		el.removeEventListener(type, fn, !!capture);	};        //修正事件名	me.prefixPointerEvent = function (pointerEvent) {		return window.MSPointerEvent ? 			‘MSPointer‘ + pointerEvent.charAt(9).toUpperCase() + pointerEvent.substr(10):			pointerEvent;	};        //用来计算加速度,力回馈什么的	me.momentum = function (current, start, time, lowerMargin, wrapperSize, deceleration) {                 //current为当前位置, start为开始位置, distance就是开始走了多少距离, 		var distance = current - start,			speed = Math.abs(distance) / time,//当前的速度			destination,			duration;		deceleration = deceleration === undefined ? 0.0006 : deceleration;		destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 );		duration = speed / deceleration;		if ( destination < lowerMargin ) {			destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin;			distance = Math.abs(destination - current);			duration = distance / speed;		} else if ( destination > 0 ) {			destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0;			distance = Math.abs(current) + destination;			duration = distance / speed;		}		return {			destination: Math.round(destination),			duration: duration		};	};        //取得可以用transform样式名	var _transform = _prefixStyle(‘transform‘);	me.extend(me, {		hasTransform: _transform !== false,//判定浏览器是否支持CSS3 transform		hasPerspective: _prefixStyle(‘perspective‘) in _elementStyle,//支持透视效果		hasTouch: ‘ontouchstart‘ in window,//是否是触摸屏		hasPointer: window.PointerEvent || window.MSPointerEvent, // IE10 is prefixed		hasTransition: _prefixStyle(‘transition‘) in _elementStyle//支持CSS3渐变	});	// This should find all Android browsers lower than build 535.19 (both stock browser and webview)         //判定安卓的浏览器是否版本过低	me.isBadAndroid = /Android /.test(window.navigator.appVersion) && !(/Chrome\/\d/.test(window.navigator.appVersion));	me.extend(me.style = {}, {//修正与CSS3变渐相关的样式名		transform: _transform,		transitionTimingFunction: _prefixStyle(‘transitionTimingFunction‘),		transitionDuration: _prefixStyle(‘transitionDuration‘),		transitionDelay: _prefixStyle(‘transitionDelay‘),		transformOrigin: _prefixStyle(‘transformOrigin‘)	});        //操作类名的三个常用方法	me.hasClass = function (e, c) {		var re = new RegExp("(^|\\s)" + c + "(\\s|$)");		return re.test(e.className);	};	me.addClass = function (e, c) {		if ( me.hasClass(e, c) ) {			return;		}		var newclass = e.className.split(‘ ‘);		newclass.push(c);		e.className = newclass.join(‘ ‘);	};	me.removeClass = function (e, c) {		if ( !me.hasClass(e, c) ) {			return;		}		var re = new RegExp("(^|\\s)" + c + "(\\s|$)", ‘g‘);		e.className = e.className.replace(re, ‘ ‘);	};	me.offset = function (el) {		var left = -el.offsetLeft,			top = -el.offsetTop;		// jshint -W084		while (el = el.offsetParent) {			left -= el.offsetLeft;			top -= el.offsetTop;		}		// jshint +W084		return {			left: left,			top: top		};	};	me.preventDefaultException = function (el, exceptions) {		for ( var i in exceptions ) {			if ( exceptions[i].test(el[i]) ) {				return true;			}		}		return false;	};        //四套事件名	me.extend(me.eventType = {}, {		touchstart: 1,		touchmove: 1,		touchend: 1,		mousedown: 2,		mousemove: 2,		mouseup: 2,		pointerdown: 3,		pointermove: 3,		pointerup: 3,		MSPointerDown: 3,		MSPointerMove: 3,		MSPointerUp: 3	});        //各种缓动公式	me.extend(me.ease = {}, {		quadratic: {			style: ‘cubic-bezier(0.25, 0.46, 0.45, 0.94)‘,			fn: function (k) {				return k * ( 2 - k );			}		},		circular: {			style: ‘cubic-bezier(0.1, 0.57, 0.1, 1)‘,	// Not properly "circular" but this looks better, it should be (0.075, 0.82, 0.165, 1)			fn: function (k) {				return Math.sqrt( 1 - ( --k * k ) );			}		},		back: {			style: ‘cubic-bezier(0.175, 0.885, 0.32, 1.275)‘,			fn: function (k) {				var b = 4;				return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;			}		},		bounce: {			style: ‘‘,			fn: function (k) {				if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {					return 7.5625 * k * k;				} else if ( k < ( 2 / 2.75 ) ) {					return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;				} else if ( k < ( 2.5 / 2.75 ) ) {					return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;				} else {					return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;				}			}		},		elastic: {			style: ‘‘,			fn: function (k) {				var f = 0.22,					e = 0.4;				if ( k === 0 ) { return 0; }				if ( k == 1 ) { return 1; }				return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );			}		}	});        //触发tap事件,这是为了低消移动端点击事件有300ms延迟发明出来的自定义事件	me.tap = function (e, eventName) {		var ev = document.createEvent(‘Event‘);		ev.initEvent(eventName, true, true);		ev.pageX = e.pageX;		ev.pageY = e.pageY;		e.target.dispatchEvent(ev);	};        //触发点击事件	me.click = function (e) {		var target = e.target,			ev;		if ( !(/(SELECT|INPUT|TEXTAREA)/i).test(target.tagName) ) {			ev = document.createEvent(‘MouseEvents‘);			ev.initMouseEvent(‘click‘, true, true, e.view, 1,				target.screenX, target.screenY, target.clientX, target.clientY,				e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,				0, null);			ev._constructed = true;			target.dispatchEvent(ev);		}	};	return me;})();

源码还算易读,但也遇到几个不懂的地方。一个是momentum方法,物理公式忘光了,另一个是offset方法。offset的结果与我们常用的不一致。

<!DOCTYPE html><html>    <head>        <title>iscroll学习 by 司徒正美</title>        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">        <script src="http://www.mamicode.com/avalon.js" ></script>        <script>            window.onload = function() {                var elem = document.querySelector("#son")                var offset = function(el) {                    var left = -el.offsetLeft,                            top = -el.offsetTop;                    // jshint -W084                    while (el = el.offsetParent) {                        left -= el.offsetLeft;                        top -= el.offsetTop;                    }                    // jshint +W084                    return {                        left: left,                        top: top                    };                };                console.log(offset(elem)) //iscroll.offset 返回 [-400,-400]                console.log(avalon(elem).offset()) //jQuery.offset, avalon.offset返回 [400, 400]                           }        </script>    </head>    <body>        <style>                      #parent{                position: absolute;                top:200px;                left:200px;            }            #son {                position: absolute;                top:200px;                left:200px;            }        </style>        <div ms-controller="test">            <div  id="parent" >                <input id="son"  />ddd               </div        </div>    </body></html>