前言

最近两天看到很多的总结性发言,我想想今年好像我的变化挺大的,是不是该晚上来水一发呢?嗯,决定了,晚上来水一发!

上周六,我们简单模拟了下iScroll的实现,周日我们开始了学习iScroll的源码,今天我们接着上次的记录学习,因为最近事情稍微有点多了

学习进度可能要放慢,而且iScroll这个库实际意义很大,不能囫囵吞枣的学习,要学到精华,并且要用于项目中的,所以初步规划是最近两周主要围绕iScroll展开

而后两个选择:① 分离iScroll代码用于项目;② 抄袭iScroll精华部分用于项目。无论如何都要用于项目......

几个事件点

iScroll的整体逻辑由三大事件点组成:

① touchStart(mousedown)

② touchMove(mousemove)

③ touchEnd(mouseUp)

也就是iScroll整体的功能逻辑其实是由这几个事件串起来的,其中

touchStart会保留一些初始化操作,或者停止正在进行的动画

touchMove会带动dom一起移动

而touchEnd最为复杂,在touchend阶段可能需要处理很多东西

  1. 一般性拖动结束事件
  2. 超出边界还原后触发的事件(此时可以滚动加载数据)
  3. 如果此次为一次按钮点击,需要触发按钮事件那么还有对preventDefault进行处理(preventDefault可能导致事件不触发)
  4. 如果此次为一次点击事件,并且对象为文本框或者select(其它会获得焦点的事件),那么应该让其获得焦点,并且弹出键盘
  5. ......

以上为主观臆测下的猜想,我们来看看iScroll实际干了些什么,下面再细细的分析各个阶段

start

  1. _start: function (e) {
  2. // React to left mouse button only
  3. if (utils.eventType[e.type] != 1) {
  4. if (e.button !== 0) {
  5. return;
  6. }
  7. }
  8. if (!this.enabled || (this.initiated && utils.eventType[e.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.touches[0] : e,
  15. pos;
  16.  
  17. this.initiated = utils.eventType[e.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. },
  1. if ( utils.eventType[e.type] != 1 ) {
  2. if ( e.button !== 0 ) {
  3. return;
  4. }
  5. }

首先一段代码特别针对非touch事件进行了处理,其中的意图暂时不明,应该是只有点击鼠标左键的情况下才会触发下面逻辑

  1. if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) ) {
  2. return;
  3. }

第二段代码做了一次是否开始的验证,this.enabled应该是程序功能总开关,this.initiated为首次触发touchStart的事件类型

可能是mousedown,touchstart或者其他,如果两次不等的话,这里也会终止流程(此段代码确实不明意图)

  1. // This should find all Android browsers lower than build 535.19 (both stock browser and webview)
  2. 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.touches[0] : e,
  2. pos;
  3.  
  4. this.initiated = utils.eventType[e.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.scrollerStyle[utils.style.transitionDuration] = time + 'ms';
  5.  
  6. if (!time && utils.isBadAndroid) {
  7. this.scrollerStyle[utils.style.transitionDuration] = '0.001s';
  8. }
  9.  
  10. if (this.indicators) {
  11. for (var i = this.indicators.length; i--; ) {
  12. this.indicators[i].transitionTime(time);
  13. }
  14. }
  15.  
  16. // INSERT POINT: _transitionTime
  17. },

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.scrollerStyle[utils.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.indicators[i].updatePosition();
  16. }
  17. }
  18. // INSERT POINT: _translate
  19. },

下面的代码依旧在操作滚动条,不用现在关注,这里将信息全部写入了scrollerStyle对象,同事dom style属性的引用,这里就直接给赋值了

然我们来看看this._execEvent('scrollEnd');这段代码

首先_execEvent是用于触发存储在this._event数组中的事件的方法,然后我们只看scrollEnd即可,这里的事件机制,我们提到后面来说

很奇怪的是,这里有触发事件的代码却没有注册的代码,这是因为这个接口应该是开放给用户的,然后这里的beforeScrollStart也是开放给用户注册事件的

到此touchstart相关事件就结束了,我们接下来看move事件

move

  1. _move: function (e) {
  2. if (!this.enabled || utils.eventType[e.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.touches[0] : 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 initiate
  26. if (timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10)) {
  27. return;
  28. }
  29.  
  30. // If you are scrolling in one direction lock the other
  31. if (!this.directionLocked && !this.options.freeScroll) {
  32. if (absDistX > absDistY + this.options.directionLockThreshold) {
  33. this.directionLocked = 'h'; // lock horizontally
  34. } else if (absDistY >= absDistX + this.options.directionLockThreshold) {
  35. this.directionLocked = 'v'; // lock vertically
  36. } else {
  37. this.directionLocked = 'n'; // no lock
  38. }
  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 boundaries
  68. if (newX > 0 || newX < this.maxScrollX) {
  69. newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX;
  70. }
  71. if (newY > 0 || newY < this.maxScrollY) {
  72. newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY;
  73. }
  74.  
  75. this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
  76. this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;
  77.  
  78. if (!this.moved) {
  79. this._execEvent('scrollStart');
  80. }
  81.  
  82. this.moved = true;
  83.  
  84. this._translate(newX, newY);
  85.  
  86. /* REPLACE START: _move */
  87.  
  88. if (timestamp - this.startTime > 300) {
  89. this.startTime = timestamp;
  90. this.startX = this.x;
  91. this.startY = this.y;
  92. }
  93.  
  94. /* REPLACE END: _move */
  95.  
  96. },

move为功能第二个阶段,首先仍然是做全局开关检测,如果不通过就直接给干掉

  1. if (!this.enabled || utils.eventType[e.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.touches[0] : 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 horizontally
  4. } else if (absDistY >= absDistX + this.options.directionLockThreshold) {
  5. this.directionLocked = 'v'; // lock vertically
  6. } else {
  7. this.directionLocked = 'n'; // no lock
  8. }
  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 < this.maxScrollX) {
  6. newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX;
  7. }
  8. if (newY > 0 || newY < this.maxScrollY) {
  9. newY = this.options.bounce ? this.y + deltaY / 3 : 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,这种封装的思想很不错的,值得借鉴,现在就进入关键点了touchend

end

  1. _end: function (e) {
  2. if (!this.enabled || utils.eventType[e.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.changedTouches[0] : 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 boundaries
  26. if (this.resetPosition(this.options.bounceTime)) {
  27. return;
  28. }
  29.  
  30. this.scrollTo(newX, newY); // ensures that the last position is rounded
  31.  
  32. // we scrolled less than 10 pixels
  33. 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 needed
  52. if (this.options.momentum && duration < 300) {
  53. momentumX = this.hasHorizontalScroll ? utils.momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0) : { destination: newX, duration: 0 };
  54. momentumY = this.hasVerticalScroll ? utils.momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0) : { destination: newY, duration: 0 };
  55. newX = momentumX.destination;
  56. newY = momentumY.destination;
  57. time = Math.max(momentumX.duration, momentumY.duration);
  58. this.isInTransition = 1;
  59. }
  60.  
  61. if (this.options.snap) {
  62. var snap = this._nearestSnap(newX, newY);
  63. this.currentPage = snap;
  64. time = this.options.snapSpeed || Math.max(
  65. Math.max(
  66. Math.min(Math.abs(newX - snap.x), 1000),
  67. Math.min(Math.abs(newY - snap.y), 1000)
  68. ), 300);
  69. newX = snap.x;
  70. newY = snap.y;
  71.  
  72. this.directionX = 0;
  73. this.directionY = 0;
  74. easing = this.options.bounceEasing;
  75. }
  76.  
  77. // INSERT POINT: _end
  78.  
  79. if (newX != this.x || newY != this.y) {
  80. // change easing function when scroller goes out of the boundaries
  81. if (newX > 0 || newX < this.maxScrollX || newY > 0 || newY < this.maxScrollY) {
  82. easing = utils.ease.quadratic;
  83. }
  84.  
  85. this.scrollTo(newX, newY, time, easing);
  86. return;
  87. }
  88.  
  89. this._execEvent('scrollEnd');
  90. },

开始我们就说过,touchend为这个控件一个关键点与难点,现在我们就来啃一啃,这个看完了,iScroll核心部分也就结束了,后面就只需要拆解分析即可

首先仍然是一点初始化操作

  1. if (!this.enabled || utils.eventType[e.type] !== this.initiated) {
  2. return;
  3. }
  4.  
  5. if (this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException)) {
  6. e.preventDefault();
  7. }

而后逐步好戏上场了,在手指离开前做了状态保存

  1. var point = e.changedTouches ? e.changedTouches[0] : e,
  2. momentumX,
  3. momentumY,
  4. duration = utils.getTime() - this.startTime,
  5. newX = Math.round(this.x),
  6. newY = Math.round(this.y),
  7. distanceX = Math.abs(newX - this.startX),
  8. distanceY = Math.abs(newY - this.startY),
  9. time = 0,
  10. easing = '';
  11.  
  12. this.isInTransition = 0;
  13. this.initiated = 0;
  14. this.endTime = utils.getTime();

duration是当前拖动的事件,这里可不是手指触屏到离开哦,因为move时候每300ms变了一次

PS:这里想象一下如果我们想要快速的滑动是不是触屏屏幕很快呢,而我们一直拖动DOM在最后也是有可能想让他快速移动的

记录当前x,y位置记录当前移动位置distanceY,然后重置结束时间,这里有一个resetPosition方法:

  1. resetPosition: function (time) {
  2. var x = this.x,
  3. y = this.y;
  4.  
  5. time = time || 0;
  6.  
  7. if ( !this.hasHorizontalScroll || this.x > 0 ) {
  8. x = 0;
  9. } else if ( this.x < this.maxScrollX ) {
  10. x = this.maxScrollX;
  11. }
  12.  
  13. if ( !this.hasVerticalScroll || this.y > 0 ) {
  14. y = 0;
  15. } else if ( this.y < this.maxScrollY ) {
  16. y = this.maxScrollY;
  17. }
  18.  
  19. if ( x == this.x && y == this.y ) {
  20. return false;
  21. }
  22.  
  23. this.scrollTo(x, y, time, this.options.bounceEasing);
  24.  
  25. return true;
  26. },

他是记录我们是不是已经离开了边界了,如果离开边界了就不会执行后面逻辑,而直接重置DOM位置,这里还用到了我们的scrollTo方法,该方法尤其关键

scrollTo

  1. scrollTo: function (x, y, time, easing) {
  2. easing = easing || utils.ease.circular;
  3.  
  4. this.isInTransition = this.options.useTransition && time > 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事件

  1. transitionend 事件会在 CSS transition 结束后触发. transition完成前移除transition时,比如移除csstransition-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._execEvent('scrollEnd');
  10. }
  11. },

所以,我们第二次touchstart时候,便高高兴兴停止了动画(之一_transitionTime未传time时候会重置时间),所以先取消动画再移动位置

于是继续回到我们的end事件,

  1. this.scrollTo(newX, newY);

如果没有超出边界便滑动到应该去的位置(这里有动画哦)

点击情况

当然,我们手指可能当前只不过想点击而已,这个时候就要触发相关的点击事件了,如果需要获取焦点,便获取焦点

PS:他这里还模拟的fastclick想提升响应速度,但是他这样会引起大量BUG

  1. if (!this.moved) {
  2. if (this.options.tap) {
  3. utils.tap(e, this.options.tap);
  4. }
  5.  
  6. if (this.options.click) {
  7. utils.click(e);
  8. }
  9.  
  10. this._execEvent('scrollCancel');
  11. return;
  12. }
  13.  
  14. if (this._events.flick && duration < 200 && distanceX < 100 && distanceY < 100) {
  15. this._execEvent('flick');
  16. return;
  17. }

运动参数

第一步的scrollTo其实可以放到move里面去,后面就用到了我们上文所说,根据动力加速度计算出来的动画参数:

  1. if (this.options.momentum && duration < 300) {
  2. momentumX = this.hasHorizontalScroll ? utils.momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0) : { destination: newX, duration: 0 };
  3. momentumY = this.hasVerticalScroll ? utils.momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0) : { destination: newY, duration: 0 };
  4. newX = momentumX.destination;
  5. newY = momentumY.destination;
  6. time = Math.max(momentumX.duration, momentumY.duration);
  7. this.isInTransition = 1;
  8. }

那个snap不必关注,直接看下面,在此使用

  1. this.scrollTo(newX, newY, time, easing);

开始运动,最后触发scrollend事件,这里如果超出边界会执行resetPosition方法还原的,不必关心

由此,我们几大核心事件点便学习结束了,轻松愉快哈

结语

今天学习了iScroll的几个核心点,我们下次来说下他的滚动条以及事件机制相关,整个iScroll就七七八八了

【iScroll源码学习02】分解iScroll三个核心事件点的更多相关文章

  1. 【iScroll源码学习03】iScroll事件机制与滚动条的实现

    前言 想不到又到周末了,周末的时间要抓紧学习才行,前几天我们学习了iScroll几点基础知识: 1. [iScroll源码学习02]分解iScroll三个核心事件点 2. [iScroll源码学习01 ...

  2. 【iScroll源码学习04】分离IScroll核心

    前言 最近几天我们前前后后基本将iScroll源码学的七七八八了,文章中未涉及的各位就要自己去看了 1. [iScroll源码学习03]iScroll事件机制与滚动条的实现 2. [iScroll源码 ...

  3. 【iScroll源码学习00】模拟iScroll

    前言 相信对移动端有了解的朋友对iScroll这个库非常熟悉吧,今天我们就来说下我们移动页面的iScroll化 iScroll是我们必学框架之一,我们这次先根据iScroll功能自己实现其功能,然后再 ...

  4. 【iScroll源码学习01】准备阶段 - 叶小钗

    [iScroll源码学习01]准备阶段 - 叶小钗 时间 2013-12-29 18:41:00 博客园-原创精华区 原文  http://www.cnblogs.com/yexiaochai/p/3 ...

  5. wemall app商城源码中android按钮的三种响应事件

    wemall-mobile是基于WeMall的android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.本文分享wemall app商城源码中android按 ...

  6. 【Netty源码学习】DefaultChannelPipeline(三)

    上一篇博客中[Netty源码学习]ChannelPipeline(二)我们介绍了接口ChannelPipeline的提供的方法,接下来我们分析一下其实现类DefaultChannelPipeline具 ...

  7. 【iScroll源码学习01】准备阶段

    前言 我们昨天初步了解了为什么会出现iScroll:[SPA]移动站点APP化研究之上中下页面的iScroll化(上),然后简单的写了一个demo来模拟iScroll,其中了解到了以下知识点: ① v ...

  8. iscroll源码学习(1)

    iscroll是移端端开发的两大利器之一(另一个是fastclick),为了将它整合的avalon,需要对它认真学习一番.下面是我的笔记. 第一天看的是它的工具类util.js //用于做函数节流 v ...

  9. 【requireJS源码学习02】data-main加载的实现

    前言 经过昨天的学习,我们大概了解到了requireJS的主要结构,这里先大概的回顾一下 首先从总体结构来说,require这里分为三块: ① newContext之前变量声明或者一些工具函数 ② n ...

随机推荐

  1. android 手机开启debug日志

    来自 http://blog.csdn.net/aikongmeng/article/details/9764297 真机默认是不开启Log 开关的,这么来说我们如果使用真机来搞程序测试的话,需要做以 ...

  2. 【博客美化】04.自定义地址栏logo

    博客园美化相关文章目录: [博客美化]01.推荐和反对炫酷样式 [博客美化]02.公告栏显示个性化时间 [博客美化]03.分享按钮 [博客美化]04.自定义地址栏logo [博客美化]05.添加Git ...

  3. 移动web开发之视口viewport

    × 目录 [1]布局视口 [2]视觉视口 [3]理想视口[4]meta标签[5]总结 前面的话 在CSS标准文档中,视口viewport被称为初始包含块.这个初始包含块是所有CSS百分比宽度推算的根源 ...

  4. CentOS yum安装Apache + PHP + Tomcat7 + MySQL

    Linux平台上用得最多的web环境就是php.java和MySQL了,会搭建这个环境,就能把很多开源程序跑起来. 作为一个程序猿,虽然并不用精通运维的活,但基本的Linux环境搭建还是要掌握比较好, ...

  5. [OpenCV] Samples 10: imagelist_creator

    yaml写法的简单例子.将 $ ./ 1 2 3 4 5 命令的参数(代表图片地址)写入yaml中. 写yaml文件. 参考:[OpenCV] Samples 06: [ML] logistic re ...

  6. 用Log Parser Studio分析IIS日志

    发现一个强大的图形化IIS日志分析工具——Log Parser Studio,下面分享一个实际操作案例. 1. 安装Log Parser Studio a) 需要先安装Log Parser,下载地址: ...

  7. [Node.js] 闭包和高阶函数

    原文地址:http://www.moye.me/2014/12/29/closure_higher-order-function/ 引子 最近发现一个问题:一部分写JS的人,其实对于函数式编程的概念并 ...

  8. JavaScript及兼容性笔记

    1. Json to String JSON.stringify(jsonobj)//(IE8+,Chrome 1+,FF 3+) //参考 http://www.nowamagic.net/libr ...

  9. 白话Https

    本文试图以通俗易通的方式介绍Https的工作原理,不纠结具体的术语,不考证严格的流程.我相信弄懂了原理之后,到了具体操作和实现的时候,方向就不会错,然后条条大路通罗马.阅读文本需要提前大致了解对称加密 ...

  10. 基于HT for Web的Web SCADA工控移动应用

    在电力.油田燃气.供水管网等工业自动化领域Web SCADA的概念已经提出了多年,早先年的Web SCADA前端技术大部分还是基于Flex.Silverlight甚至Applet这样的重客户端方案,在 ...