资讯专栏INFORMATION COLUMN

iscroll-lite源码阅读

jackzou / 2568人阅读

摘要:最后调用函数来判断是否需要重置位置保存事件的类型,如果跟已经保存的事件类型不是同一类,不继续执行函数。如果在滚动状态,调用函数来获取的位置,调用方法让停在当前位置。主要是选择动画效果,调用或是来进行滚动根据所选的动画效果,在每一个下调用函数

背景

最近开发移动端项目用到了iscroll,踩了不少坑,因此阅读源码加深下对这个工具的了解。
本次阅读的是iscroll-lite,包含了主要的功能,比完整版少了鼠标滚轮,滚动后对齐到特定位置,按键绑定及定制滚动条等功能。

主体结构

以当前的5.2版本为例,前三百行是工具函数,封装了一些工具。三百行到四百行是构造函数IScroll,起到配置和初始化的效果。四百行到一千修改了IScroll的原型,给IScroll实例加上了各种方法。最后是模块化相关的判断。总体的结构是很清晰的。

工具函数
_prefixStyle

用于浏览器兼容。

</>复制代码

  1. var _vendor = (function () {
  2. var vendors = ["t", "webkitT", "MozT", "msT", "OT"],
  3. transform,
  4. i = 0,
  5. l = vendors.length;
  6. for ( ; i < l; i++ ) {
  7. transform = vendors[i] + "ransform";
  8. if ( transform in _elementStyle ) return vendors[i].substr(0, vendors[i].length-1);
  9. }
  10. return false;
  11. })();
  12. function _prefixStyle (style) {
  13. if ( _vendor === false ) return false;
  14. if ( _vendor === "" ) return style;
  15. return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
  16. }
能力检测

判断浏览器是否支持相关的特性

</>复制代码

  1. me.extend(me, {
  2. hasTransform: _transform !== false,
  3. hasPerspective: _prefixStyle("perspective") in _elementStyle,
  4. hasTouch: "ontouchstart" in window,
  5. hasPointer: !!(window.PointerEvent || window.MSPointerEvent), // IE10 is prefixed
  6. hasTransition: _prefixStyle("transition") in _elementStyle
  7. });

还有一些事件监听方面的处理函数,处理class的函数,处理惯性的函数momentum

父元素的位置

</>复制代码

  1. me.offset = function (el) {
  2. var left = -el.offsetLeft,
  3. top = -el.offsetTop;
  4. // jshint -W084
  5. while (el = el.offsetParent) {
  6. left -= el.offsetLeft;
  7. top -= el.offsetTop;
  8. }
  9. // jshint +W084
  10. return {
  11. left: left,
  12. top: top
  13. };
  14. };
对事件进行分类

</>复制代码

  1. me.extend(me.eventType = {}, {
  2. touchstart: 1,
  3. touchmove: 1,
  4. touchend: 1,
  5. ...略...
  6. MSPointerDown: 3,
  7. MSPointerMove: 3,
  8. MSPointerUp: 3
  9. });
动画处理的函数

</>复制代码

  1. me.extend(me.ease = {}, {
  2. quadratic: {
  3. style: "cubic-bezier(0.25, 0.46, 0.45, 0.94)",
  4. fn: function (k) {
  5. return k * ( 2 - k );
  6. }
  7. },
  8. ....略....
  9. });
tap和click的事件模拟

</>复制代码

  1. me.tap = function (e, eventName) {
  2. var ev = document.createEvent("Event");
  3. ev.initEvent(eventName, true, true);
  4. ev.pageX = e.pageX;
  5. ev.pageY = e.pageY;
  6. e.target.dispatchEvent(ev);
  7. };
  8. me.click = function (e) {
  9. var target = e.target,
  10. ev;
  11. if ( !(/(SELECT|INPUT|TEXTAREA)/i).test(target.tagName) ) {
  12. // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/initMouseEvent
  13. // initMouseEvent is deprecated.
  14. ev = document.createEvent(window.MouseEvent ? "MouseEvents" : "Event");
  15. ev.initEvent("click", true, true);
  16. ...略...
  17. target.dispatchEvent(ev);
  18. }
  19. };
touchAction的设定

</>复制代码

  1. me.getTouchAction = function(eventPassthrough, addPinch) {
  2. var touchAction = "none";
  3. if ( eventPassthrough === "vertical" ) {
  4. touchAction = "pan-y";
  5. } else if (eventPassthrough === "horizontal" ) {
  6. touchAction = "pan-x";
  7. }
  8. if (addPinch && touchAction != "none") {
  9. // add pinch-zoom support if the browser supports it, but if not (eg. Chrome <55) do nothing
  10. touchAction += " pinch-zoom";
  11. }
  12. return touchAction;
  13. };
元素的位置

</>复制代码

  1. me.getRect = function(el) {
  2. if (el instanceof SVGElement) {
  3. var rect = el.getBoundingClientRect();
  4. return {
  5. top : rect.top,
  6. left : rect.left,
  7. width : rect.width,
  8. height : rect.height
  9. };
  10. } else {
  11. return {
  12. top : el.offsetTop,
  13. left : el.offsetLeft,
  14. width : el.offsetWidth,
  15. height : el.offsetHeight
  16. };
  17. }
  18. };
构造函数

先是获取了wrapper和scroller,接着是一些能力检测。

在这里通过eventPassthrough,scrollY,scrollX,freeScroll来配置滚动的方向。只有scrollY是默认为true,其他都是未设定undefined.
当eventPassthrough的值为vertical时,在垂直方向不使用iscroll的滚动,使用原生的滚动。为horizontal时,则水平方向如此。想要二维自由滚动,不能设置该值。

</>复制代码

  1. this.options.eventPassthrough = this.options.eventPassthrough === true ? "vertical" : this.options.eventPassthrough;
  2. this.options.scrollY = this.options.eventPassthrough == "vertical" ? false : this.options.scrollY;
  3. this.options.scrollX = this.options.eventPassthrough == "horizontal" ? false : this.options.scrollX;
  4. this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough;

接着还有其他属性的设定,最后调用_init,refresh,scrollTo,enable等函数

一些原形上绑定的函数
_init

只调用了_initEvents函数

_initEvents

在这里根据配置给wrapper或是window加入了鼠标事件,pointer事件和触摸事件,transition事件等事件的监听。这里用到了一种少见的事件绑定方法,在这里事件绑定的第三个参数不是常见的function,而是iscroll对象。而iscroll对象里有一个方法handleEvent,这种方式的好处就是可以传递this

</>复制代码

  1. handleEvent: function (e) {
  2. switch ( e.type ) {
  3. case "touchstart":
  4. case "pointerdown":
  5. case "MSPointerDown":
  6. case "mousedown":
  7. this._start(e);
  8. break;
  9. case "touchmove":
  10. case "pointermove":
  11. case "MSPointerMove":
  12. case "mousemove":
  13. this._move(e);
  14. break;
  15. case "touchend":
  16. case "pointerup":
  17. case "MSPointerUp":
  18. case "mouseup":
  19. case "touchcancel":
  20. case "pointercancel":
  21. case "MSPointerCancel":
  22. case "mousecancel":
  23. this._end(e);
  24. ...略...
  25. }
  26. }
refresh

保存wrapper和scroller的尺寸信息,及wrapper的位置信息。设定最大滚动距离,为负数。
接着判断是否存在水平滚动或是垂直滚动

</>复制代码

  1. this.maxScrollX = this.wrapperWidth - this.scrollerWidth;
  2. this.hasHorizontalScroll = this.options.scrollX && this.maxScrollX < 0;

如果浏览器支持pointer,使用touchAction来限制wrapper。
最后调用resetPosition函数来判断是否需要重置位置

_start

保存事件的类型,如果跟已经保存的事件类型不是同一类,不继续执行函数。
如果scroller在滚动状态,调用getComputedPosition函数来获取scroller的位置,调用_translate方法让scroller停在当前位置。
保存当前的位置信息和触发事件的位置信息,设置状态信息

</>复制代码

  1. this.initiated = utils.eventType[e.type];
  2. this.moved = false;
  3. this.distX = 0;
  4. this.distY = 0;
  5. this.directionX = 0;
  6. this.directionY = 0;
  7. this.directionLocked = 0;
  8. this.startX = this.x;
  9. this.startY = this.y;
  10. this.absStartX = this.x;
  11. this.absStartY = this.y;
  12. this.pointX = point.pageX;
  13. this.pointY = point.pageY;
_move

获取事件的位置信息,对比_start中保存的位置信息,计算偏移值

</>复制代码

  1. var point = e.touches ? e.touches[0] : e,
  2. deltaX = point.pageX - this.pointX,
  3. deltaY = point.pageY - this.pointY,

保存新的位置信息,位移信息

</>复制代码

  1. this.pointX = point.pageX;
  2. this.pointY = point.pageY;
  3. this.distX += deltaX;
  4. this.distY += deltaY;

如果偏移小于10像素不执行滚动。偏移的大小值和设定来根据判断滚动的方向。最后调用_translate函数来滚动

_translate

根据设定,修改滚动条中的transform的值或是修改left和top的值来进行具体的滚动,保存信息到this.x和this.y

</>复制代码

  1. if ( this.options.useTransform ) {
  2. this.scrollerStyle[utils.style.transform] = "translate(" + x + "px," + y + "px)" + this.translateZ;
  3. } else {
  4. x = Math.round(x);
  5. y = Math.round(y);
  6. this.scrollerStyle.left = x + "px";
  7. this.scrollerStyle.top = y + "px";
  8. }
_end

让scroller滚动到整数位置,根据位移和触发事件的时间的情况,摸你tap事件、click事件或是执行flick事件或调用scrollTO函数进行惯性滚动。

scrollTo

主要是选择动画效果,调用__translate或是_animate来进行滚动

_animate

根据所选的动画效果,在每一个requestAnimationFrame下调用_translate函数

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94216.html

相关文章

  • 一篇文章把本该属于你的源码天赋还给你

    摘要:一些方法不应该这样不应该漫无目的地随手拿起一分源码,试图去通读。应该这样精心挑选要阅读的源码项目。这最好是与你的编程语言你的工作内容你的兴趣所在相关的,这样才能更切实地感受到阅读源码给你带来的益处,更有动力继续。 showImg(https://segmentfault.com/img/bVbcvmm?w=785&h=525); 怎么阅读源码 没有经验的技术差底子薄的初级程序员,如何阅...

    chanjarster 评论0 收藏0
  • 精读《源码学习》

    摘要:精读原文介绍了学习源码的两个技巧,并利用实例说明了源码学习过程中可以学到许多周边知识,都让我们受益匪浅。讨论地址是精读源码学习如果你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。 1. 引言 javascript-knowledge-reading-source-code 这篇文章介绍了阅读源码的重要性,精读系列也已有八期源码系列文章,分别是: 精读《Immer.js》源...

    aboutU 评论0 收藏0
  • android高级工程师-阅读源码的经验总结

    摘要:对于一个有追求的程序员来说必须读源码,当然阅读源码是一件令人头疼的事。和差不多,想法一样,但是和相比,搜索到的有价值结果不是很多,没有的理想,还有一些来着百度知道。 对于一个有追求的程序员来说必须读源码,当然阅读源码是一件令人头疼的事。阅读别人的代码远比自己写代码要难。Linus 是Linux的早期作者,一句影响深远的话是Read The Fucking Source Code(RTF...

    idisfkj 评论0 收藏0

发表评论

0条评论

jackzou

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<