【iScroll源码学习02】分解iScroll三个核心事件点_第1页
【iScroll源码学习02】分解iScroll三个核心事件点_第2页
【iScroll源码学习02】分解iScroll三个核心事件点_第3页
【iScroll源码学习02】分解iScroll三个核心事件点_第4页
【iScroll源码学习02】分解iScroll三个核心事件点_第5页
已阅读5页,还剩13页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

前言最近两天看到很多的总结性发言,我想想今年好像我的变化挺大的,是不是该晚上来水一发呢?嗯,决定了,晚上来水一发!上周六,我们简单模拟了下iScroll的实现,周日我们开始了学习iScroll的源码,今天我们接着上次的记录学习,因为最近事情稍微有点多了学习进度可能要放慢,而且iScroll这个库实际意义很大,不能囫囵吞枣的学习,要学到精华,并且要用于项目中的,所以初步规划是最近两周主要围绕iScroll展开而后两个选择: 分离iScroll代码用于项目; 抄袭iScroll精华部分用于项目。无论如何都要用于项目.几个事件点iScroll的整体逻辑由三大事件点组成: touchStart(mousedown) touchMove(mousemove) touchEnd(mouseUp)也就是iScroll整体的功能逻辑其实是由这几个事件串起来的,其中touchStart会保留一些初始化操作,或者停止正在进行的动画touchMove会带动dom一起移动而touchEnd最为复杂,在touchend阶段可能需要处理很多东西 一般性拖动结束事件 超出边界还原后触发的事件(此时可以滚动加载数据) 如果此次为一次按钮点击,需要触发按钮事件那么还有对preventDefault进行处理(preventDefault可能导致事件不触发) 如果此次为一次点击事件,并且对象为文本框或者select(其它会获得焦点的事件),那么应该让其获得焦点,并且弹出键盘 .以上为主观臆测下的猜想,我们来看看iScroll实际干了些什么,下面再细细的分析各个阶段start 1 _start: function (e) 2 / React to left mouse button only 3 if (utils.eventTypee.type != 1) 4 if (e.button != 0) 5 return; 6 7 8 if (!this.enabled | (this.initiated & utils.eventTypee.type != this.initiated) 9 return;10 11 if (this.options.preventDefault & !utils.isBadAndroid & !utils.preventDefaultException(e.target, this.options.preventDefaultException) 12 e.preventDefault();13 14 var point = e.touches ? e.touches0 : e,15 pos;16 17 this.initiated = utils.eventTypee.type;18 this.moved = false;19 this.distX = 0;20 this.distY = 0;21 this.directionX = 0;22 this.directionY = 0;23 this.directionLocked = 0;24 25 this._transitionTime();26 27 this.startTime = utils.getTime();28 29 if (this.options.useTransition & this.isInTransition) 30 this.isInTransition = false;31 pos = this.getComputedPosition();32 this._translate(Math.round(pos.x), Math.round(pos.y);33 this._execEvent(scrollEnd);34 else if (!this.options.useTransition & this.isAnimating) 35 this.isAnimating = false;36 this._execEvent(scrollEnd);37 38 39 this.startX = this.x;40 this.startY = this.y;41 this.absStartX = this.x;42 this.absStartY = this.y;43 this.pointX = point.pageX;44 this.pointY = point.pageY;45 46 this._execEvent(beforeScrollStart);47 ,View Code 1 if ( utils.eventTypee.type != 1 ) 2 if ( e.button != 0 ) 3 return; 4 5 首先一段代码特别针对非touch事件进行了处理,其中的意图暂时不明,应该是只有点击鼠标左键的情况下才会触发下面逻辑1 if ( !this.enabled | (this.initiated & utils.eventTypee.type != this.initiated) ) 2 return;3 第二段代码做了一次是否开始的验证,this.enabled应该是程序功能总开关,this.initiated为首次触发touchStart的事件类型可能是mousedown,touchstart或者其他,如果两次不等的话,这里也会终止流程(此段代码确实不明意图)/ 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);1 if ( this.options.preventDefault & !utils.isBadAndroid & !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) 2 e.preventDefault();3 第三段做了一些兼容性操作,各位可以看到只有这里有preventDefault的操作,如果默写dom元素在你手触碰会有默认的事件,比如文本框,便会触发但是我们默认是阻止的,而在一些android设备上市必须阻止的,这里建议都阻止 1 var point = e.touches ? e.touches0 : e, 2 pos; 3 4 this.initiated = utils.eventTypee.type; 5 this.moved = false; 6 this.distX = 0; 7 this.distY = 0; 8 this.directionX = 0; 9 this.directionY = 0;10 this.directionLocked = 0;接下来进行了一些初始化属性的定义,其中比较重要的是 point做了最简单的兼容性处理 this.moved你想知道现在控件是不是在拖动就看他了然后这里执行了一个方法:_transitionTime 1 _transitionTime: function (time) 2 time = time | 0; 3 4 this.scrollerStyleutils.style.transitionDuration = time + ms; 5 6 if (!time & utils.isBadAndroid) 7 this.scrollerStyleutils.style.transitionDuration = 0.001s; 8 9 10 if (this.indicators) 11 for (var i = this.indicators.length; i-; ) 12 this.indicatorsi.transitionTime(time);13 14 15 16 / INSERT POINT: _transitionTime17 ,utils具有以下style属性:1 me.extend(me.style = , 2 transform: _transform,3 transitionTimingFunction: _prefixStyle(transitionTimingFunction),4 transitionDuration: _prefixStyle(transitionDuration),5 transitionDelay: _prefixStyle(transitionDelay),6 transformOrigin: _prefixStyle(transformOrigin)7 );这里为其设置了适合放弃浏览器的运动时间属性,就是简单的兼容处理在有些android浏览器上,这个使用是有问题的,所以就直接当没传时间,直接给了个0.001s其中的indicators就是我们的滚动条,这里既然涉及到了,我们也暂时不管,因为涉及到滚动条的篇幅也不小,我们暂时不关注这个方法也涉及滚动条相关,我们这里先简单提一下,后面在补充,现在继续看下面的逻辑再下面就开始真正初始化信息,这些信息在以下会被用到 1 this.startTime = utils.getTime(); 2 3 if ( this.options.useTransition & this.isInTransition ) 4 this.isInTransition = false; 5 pos = this.getComputedPosition(); 6 this._translate(Math.round(pos.x), Math.round(pos.y); 7 this._execEvent(scrollEnd); 8 else if ( !this.options.useTransition & this.isAnimating ) 9 this.isAnimating = false;10 this._execEvent(scrollEnd);11 12 13 this.startX = this.x;14 this.startY = this.y;15 this.absStartX = this.x;16 this.absStartY = this.y;17 this.pointX = point.pageX;18 this.pointY = point.pageY;首先记录了手指触屏屏幕的时间,而后记录手指所处的位置,其中有两个if语句需要我们注意,这里的代码还是相当关键的这段话的意义是告诉我们,如果我们当前正在运动,而此时触屏了,那么就触发scrollEnd事件停止动画(这里非常关键)其中若是使用了CSS3的属性实现动画会做一些特别的处理,这里的this.isAnimating = false 是一个关键点,各位要注意他在首次为undefined(我觉得这种属性应该给他一个初始化值false),运动过程中为true,运动结束为false这里再提一下若是使用CSS3的话,会马上让dom移动到特定位置,然后停止动画 1 _translate: function (x, y) 2 if ( this.options.useTransform ) 3 this.scrollerStyleutils.style.transform = translate( + x + px, + y + px) + this.translateZ; 4 5 else 6 x = Math.round(x); 7 y = Math.round(y); 8 this.scrollerStyle.left = x + px; 9 this.scrollerStyle.top = y + px;10 11 this.x = x;12 this.y = y;13 if ( this.indicators ) 14 for ( var i = this.indicators.length; i-; ) 15 this.indicatorsi.updatePosition();16 17 18 / INSERT POINT: _translate19 ,下面的代码依旧在操作滚动条,不用现在关注,这里将信息全部写入了scrollerStyle对象,同事dom style属性的引用,这里就直接给赋值了然我们来看看this._execEvent(scrollEnd);这段代码首先_execEvent是用于触发存储在this._event数组中的事件的方法,然后我们只看scrollEnd即可,这里的事件机制,我们提到后面来说很奇怪的是,这里有触发事件的代码却没有注册的代码,这是因为这个接口应该是开放给用户的,然后这里的beforeScrollStart也是开放给用户注册事件的到此touchstart相关事件就结束了,我们接下来看move事件move 1 _move: function (e) 2 if (!this.enabled | utils.eventTypee.type != this.initiated) 3 return; 4 5 6 if (this.options.preventDefault) / increases performance on Android? TODO: check! 7 e.preventDefault(); 8 9 10 var point = e.touches ? e.touches0 : e,11 deltaX = point.pageX - this.pointX,12 deltaY = point.pageY - this.pointY,13 timestamp = utils.getTime(),14 newX, newY,15 absDistX, absDistY;16 17 this.pointX = point.pageX;18 this.pointY = point.pageY;19 20 this.distX += deltaX;21 this.distY += deltaY;22 absDistX = Math.abs(this.distX);23 absDistY = Math.abs(this.distY);24 25 / We need to move at least 10 pixels for the scrolling to initiate26 if (timestamp - this.endTime 300 & (absDistX 10 & absDistY absDistY + this.options.directionLockThreshold) 33 this.directionLocked = h; / lock horizontally34 else if (absDistY = absDistX + this.options.directionLockThreshold) 35 this.directionLocked = v; / lock vertically36 else 37 this.directionLocked = n; / no lock38 39 40 41 if (this.directionLocked = h) 42 if (this.options.eventPassthrough = vertical) 43 e.preventDefault();44 else if (this.options.eventPassthrough = horizontal) 45 this.initiated = false;46 return;47 48 49 deltaY = 0;50 else if (this.directionLocked = v) 51 if (this.options.eventPassthrough = horizontal) 52 e.preventDefault();53 else if (this.options.eventPassthrough = vertical) 54 this.initiated = false;55 return;56 57 58 deltaX = 0;59 60 61 deltaX = this.hasHorizontalScroll ? deltaX : 0;62 deltaY = this.hasVerticalScroll ? deltaY : 0;63 64 newX = this.x + deltaX;65 newY = this.y + deltaY;66 67 / Slow down if outside of the boundaries68 if (newX 0 | newX 0 ? 0 : this.maxScrollX;70 71 if (newY 0 | newY 0 ? 0 : this.maxScrollY;73 74 75 this.directionX = deltaX 0 ? -1 : deltaX 0 ? -1 : deltaY 300) 89 this.startTime = timestamp;90 this.startX = this.x;91 this.startY = this.y;92 93 94 /* REPLACE END: _move */95 96 ,View Code move为功能第二个阶段,首先仍然是做全局开关检测,如果不通过就直接给干掉1 if (!this.enabled | utils.eventTypee.type != this.initiated) 2 return;3 4 5 if (this.options.preventDefault) / increases performance on Android? TODO: check!6 e.preventDefault();7 然后这个时候必须要将浏览器默认事件搞掉,否则你滚动时候,如果body跟着一起滚动就麻烦了PS:Android下有些浏览器preventDefault并不能阻止浏览器滚动,这事情很烦接下来就是记录当前移动数据,为dom移动做准备了: 1 var point = e.touches ? e.touches0 : e, 2 deltaX = point.pageX - this.pointX, 3 deltaY = point.pageY - this.pointY, 4 timestamp = utils.getTime(), 5 newX, newY, 6 absDistX, absDistY; 7 8 this.pointX = point.pageX; 9 this.pointY = point.pageY;10 11 this.distX += deltaX;12 this.distY += deltaY;13 absDistX = Math.abs(this.distX);14 absDistY = Math.abs(this.distY);首先记录了当前鼠标位置,而后记录移动位置后,重置当前鼠标位置,然后这里做了一个判断,这个判断是如果我们手指一直停到一个位置不动的话,就给他终止了这里也做了一个优化,为了防止浏览器不停的重绘吗,一定是移动10px以上才真正的移动1 if (!this.directionLocked & !this.options.freeScroll) 2 if (absDistX absDistY + this.options.directionLockThreshold) 3 this.directionLocked = h; / lock horizontally4 else if (absDistY = absDistX + this.options.directionLockThreshold) 5 this.directionLocked = v; / lock vertically6 else 7 this.directionLocked = n; / no lock8 9 这里做了一个判断,让DOM朝一个方向运动即可,因为我们关注的是Y方向,这里可以暂时不予关注然后开始计算新位置了,这里要开始移动了哦(注意:这里做了一个判断如果超出边界的话,拖动率要减低) 1 newX = this.x + deltaX; 2 newY = this.y + deltaY; 3 4 / Slow down if outside of the boundaries 5 if (newX 0 | newX 0 ? 0 : this.maxScrollX; 7 8 if (newY 0 | newY 0 ? 0 : this.maxScrollY;10 我们在touchstart时候将this.moved设置为了false,这里就触发一个scrollSatrt事件后将其还原为true,所以这个事件只会触发一次,这个事件同样是开放给用户的,iScroll本身并未注册任何事件1 if (timestamp - this.startTime 300) 2 this.startTime = timestamp;3 this.startX = this.x;4 this.startY = this.y;5 每300ms会重置一次当前位置以及开始时间,这个就是为什么我们在抓住不放很久突然丢开仍然有长距离移动的原因,这个比较精妙哦!最后我们说下其中的_translate方法,这个方法用于移动DOM,这种封装的思想很不错的,值得借鉴,现在就进入关键点了touchendend 1 _end: function (e) 2 if (!this.enabled | utils.eventTypee.type != this.initiated) 3 return; 4 5 6 if (this.options.preventDefault & !utils.preventDefaultException(e.target, this.options.preventDefaultException) 7 e.preventDefault(); 8 9 10 var point = e.changedTouches ? e.changedTouches0 : e,11 momentumX,12 momentumY,13 duration = utils.getTime() - this.startTime,14 newX = Math.round(this.x),15 newY = Math.round(this.y),16 distanceX = Math.abs(newX - this.startX),17 distanceY = Math.abs(newY - this.startY),18 time = 0,19 easing = ;20 21 this.isInTransition = 0;22 this.initiated = 0;23 this.endTime = utils.getTime();24 25 / reset if we are outside of the boundaries26 if (this.resetPosition(this.options.bounceTime) 27 return;28 29 30 this.scrollTo(newX, newY); / ensures that the last position is rounded31 32 / we scrolled less than 10 pixels33 if (!this.moved) 34 if (this.options.tap) 35 utils.tap(e, this.options.tap);36 37 38 if (this.options.click) 39 utils.click(e);40 41 42 this._execEvent(scrollCancel);43 return;44 45 46 if (this._events.flick & duration 200 & distanceX 100 & distanceY 100) 47 this._execEvent(flick);48 return;49 50 51 / start momentum animation if needed52 if (this.options.momentum & duration 0 | newX 0 | newY 0 ) 8 x = 0; 9 else if ( this.x 0 ) 14 y = 0;15 else if ( this.y 0; 5 6 if ( !time | (this.options.useTransition & easing.style) ) 7 this._transitionTimingFunction(easing.style); 8 this._transitionTime(time); 9 this._translate(x, y);10 else 11 this._animate(x, y, time, easing.fn);12 13 ,这个方法是此处一个重要的方法,传入距离与时间后,他就会高高兴兴的移动到对应位置如果启用了CSS3的动画,便会使用CSS3动画方式进行动画(这个动画我们下期再说),否则使用_animate方法(js实现方案) 1 _animate: function (destX, destY, duration, easingFn) 2 var that = this, 3 startX = this.x, 4 startY = this.y, 5 startTime = utils.getTime(), 6 destTime = startTime + duration; 7 8 function step () 9 var now = utils.getTime(),10 newX, newY,11 easing;12 13 if ( now = destTime ) 14 that.isAnimating = false;15 that._translate(destX, destY);16 17 if ( !that.resetPosition(that.options.bounceTime) ) 18 that._execEvent(scrollEnd);19 20 21 return;22 23 24 now = ( now - startTime ) / duration;25 easing = easingFn(now);26 newX = ( destX - startX ) * easing + startX;27 newY = ( destY - startY ) * easing + startY;28 that._translate(newX, newY);29 30 if ( that.isAnimating ) 31 rAF(step);32 33 34 35 this.isAnimating = true;36 step();37 ,这里用到了前文说描述的settimeout实现动画方案,这里有一点需要我们回到start部分重新思考,为什么CSS停止了动画?原因是因为transitionend事件transitionend 事件会在 CSS transition 结束后触发. 当transition完成前移除transition时,比如移除css的transition-property 属性,事件将不会被触发. 1 _transitionEnd: function (e) 2 if ( e.target != this.scroller | !this.isInTransition ) 3 return; 4 5 6 this._transitionTime(); 7 if ( !this.resetPosition(this.options.bounceTime) ) 8 this.isInTransition = false; 9 this._exec

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论