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="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>