问题1:在IScroll中都是使用同样的方法对scrollbars和indicators进行初始化

 if ( this.options.scrollbars || this.options.indicators ) {this._initIndicators();}

如果配置了scrollbars和indicators都是调用_initIndicators方法来完成的
问题2:scrollX和scrollY表示的是什么?

       this.options.scrollY = this.options.eventPassthrough == 'vertical' ? false : this.options.scrollY;this.options.scrollX = this.options.eventPassthrough == 'horizontal' ? false : this.options.scrollX;

eventPassthrough表示忽略哪一个方向上的滚动,如果为vertical那么表示忽略垂直方向的滚动,这时候this.options.scrollY就是false!
问题3:如何创建滚动条

创建滚动条和滚动槽是通过下面的方法来完成的:

function createDefaultScrollbar (direction, interactive, type) {var scrollbar = document.createElement('div'),indicator = document.createElement('div');//如果含有滚动条,那么我们给滚动条设置absolute定位if ( type === true ) {scrollbar.style.cssText = 'position:absolute;z-index:9999';indicator.style.cssText = '-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;position:absolute;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);border-radius:3px';}//indicator含有className为iScrollIndicatorindicator.className = 'iScrollIndicator';//如果方向是水平的滚动条同时也有滚动条if ( direction == 'h' ) {if ( type === true ) {scrollbar.style.cssText += ';height:7px;left:2px;right:2px;bottom:0';indicator.style.height = '100%';}scrollbar.className = 'iScrollHorizontalScrollbar';} else {//如果是垂直方向的滚动条if ( type === true ) {scrollbar.style.cssText += ';width:7px;bottom:2px;top:2px;right:1px';indicator.style.width = '100%';}scrollbar.className = 'iScrollVerticalScrollbar';}scrollbar.style.cssText += ';overflow:hidden';//如果interactive为false表示不允许响应事件,那么为scrollbar元素的style添加pointerEvents为"none"就可以了,默认是""空字符串if ( !interactive ) {scrollbar.style.pointerEvents = 'none';}//scrollbar添加子元素为indicator元素scrollbar.appendChild(indicator);return scrollbar;
}

注意:其中scrollbar表示的是滚动槽,而我们的indicator表示的是滚动条,这一点要理解。直接调用这个函数就会看到效果(滚动条的高度要设置,否则默认为0)。到了这一步,滚动条就创建好了,同时append到wrapper后面就完成了。下面就会如何让滚动条在滚动槽中移动。
问题4:到底什么是indicators?

解答:滚动条(非滚动槽);自定义指示元素

看个demo源码:

<div id="viewport"><div id="wrapper"><div id="scroller"><!--scroller中的元素才是我们可以看到的元素,wrapper定宽,而scroller不定宽--><div class="slide"><div class="painting giotto"></div></div><div class="slide"><div class="painting leonardo"></div></div><div class="slide"><div class="painting gaugin"></div></div><div class="slide"><div class="painting warhol"></div></div></div></div>
</div><div id="indicator"><div id="dotty"></div>
</div>

然后我们这样使用iScroll组件:

var myScroll;
function loaded () {myScroll = new IScroll('#wrapper', {scrollX: true,scrollY: false,momentum: false,snap: true,snapSpeed: 400,keyBindings: true,//可以通过indicators来指定自己的Indicator,而滚动条也有自己的Indicator。iScroll会把两者结合起来然后逐个//创建Indicator元素indicators: {el: document.getElementById('indicator'),resize: false}});
}
document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false);

我们看看最后生成的DOM结构:


我们看看Indicator的构造函数主要做了什么:

//注意:这里创建Indicator是基于上面对滚动条的创建来完成的,其中Indicator的wrapper属性就是对滚动条的包裹元素,即scrollbar滚动槽元素的引用!
function Indicator (scroller, options) {this.wrapper = typeof options.el == 'string' ? document.querySelector(options.el) : options.el;//wrapper自己指定(此处的wrapper是Indicator对象具有的wrapper)。返回的DOM结构为<div id="scrollbar"><div id="indicator"></div></div>,也就是wrapper对象就是内部的scrollbar元素DOM。//因为这里构造的是Indicator对象,所以其wrapper当然就是scrollbar元素。如果是创建指示元素那么其wrapper就表示我们自己通过el指定this.wrapperStyle = this.wrapper.style;//scrollbar元素的style属性this.indicator = this.wrapper.children[0];//获取indticator属性,也是一个DOMthis.indicatorStyle = this.indicator.style;//获取indicator的style属性this.scroller = scroller;//indicator的scroller属性持有的就是iScroll元素的引用this.options = {listenX: true,//表示监听X轴listenY: true,//表示监听Y轴interactive: false,//可以操作resize: true,//滚动条的大小是基于wrapper和scroller的width/height来设定的,通过设置resizeScrollbars可以把滚动条设置为一个指定的大小defaultScrollbars: false,shrink: false,fade: false,//fadespeedRatioX: 0,//指示元素的移动速度是根据sroller的大小来设定的。默认情况下是自动设置的,一般yuansu不需要改变这个值speedRatioY: 0//指示元素的移动速度是根据sroller的大小来设定的。默认情况下是自动设置的,一般不需要改变这个值};//绑定listenX,listenY,speedRatioX,speedRatioY,shrink,fade属性等for ( var i in options ) {this.options[i] = options[i];}this.sizeRatioX = 1;this.sizeRatioY = 1;this.maxPosX = 0;this.maxPosY = 0;if ( this.options.interactive ) {//如果可以是touch事件,那么我们为Indicator添加touchstart,touchend事件if ( !this.options.disableTouch ) {utils.addEvent(this.indicator, 'touchstart', this);utils.addEvent(window, 'touchend', this);}//如果可以有pointer事件,我们为Indicator添加pointerdown,pointerup事件if ( !this.options.disablePointer ) {utils.addEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);utils.addEvent(window, utils.prefixPointerEvent('pointerup'), this);}//为Indicator添加mousedown,mouseup事件if ( !this.options.disableMouse ) {utils.addEvent(this.indicator, 'mousedown', this);utils.addEvent(window, 'mouseup', this);}}//如果没有操作滚动条就消失,fade对应于this.options.fadeScrollbarsif ( this.options.fade ) {//为iscrollbar元素添加transform属性,也就是启动硬件加速this.wrapperStyle[utils.style.transform] = this.scroller.translateZ;var durationProp = utils.style.transitionDuration;if(!durationProp) {return;}//为scrollbar元素添加transition-duration属性this.wrapperStyle[durationProp] = utils.isBadAndroid ? '0.0001ms' : '0ms';// remove 0.0001msvar self = this;if(utils.isBadAndroid) {rAF(function() {if(self.wrapperStyle[durationProp] === '0.0001ms') {self.wrapperStyle[durationProp] = '0s';}});}//为我们的scrollbar元素添加opaitcity,然后让它开始执行transform动画this.wrapperStyle.opacity = '0';}
}

其实在这里我们只为Indicator指定了wrapper,其对应于滚动条的滚动槽对象,而Indicator属性对应于滚动条对象,同时scroller对应于iScroll对象。同时为Indicator绑定了一系列的事件(注意是绑定到this.indicator上还是window对象上的)。 当然,还有一部分Indicator的方法全部定义在prototype上的,以后再分析
创建了Indicator后,我们需要做的就是为他绑定各种事件,不过在这之前我们看看一个方法:

          fade: function (val, hold) {//如果hold为true同时当前元素是不可见的,那么不会调用fade放啊if ( hold && !this.visible ) {return;}clearTimeout(this.fadeTimeout);this.fadeTimeout = null;var time = val ? 250 : 500,delay = val ? 0 : 300;//如果没有传递valval = val ? '1' : '0';this.wrapperStyle[utils.style.transitionDuration] = time + 'ms';//下面是一个立即执行函数this.fadeTimeout = setTimeout((function (val) {this.wrapperStyle.opacity = val;this.visible = +val;}).bind(this, val), delay);}

下面就是绑定的各种事件,如scrollCancel,scrollStart,beforeScrollStart,refresh,destroy事件等:

if ( this.options.fadeScrollbars ) {this.on('scrollEnd', function () {_indicatorsMap(function () {this.fade();//默认time是500(也就是anmation-durantion),delay为300,val(也就是opacity)为"0"(表示完全透明)。//就是使用val参数来指定animation-duration和animation-delay属性的值,其中iScroll元素的visible属性也是通过val来指定的//如果第一个参数没有指定那么就是0,否则就是1});});this.on('scrollCancel', function () {_indicatorsMap(function () {this.fade();//调用Indicator的prototype上的fade方法。});});this.on('scrollStart', function () {_indicatorsMap(function () {this.fade(1);//scrollstart表示开始滚动,这时候opacity就是1,也就是要让它显示出来});});this.on('beforeScrollStart', function () {_indicatorsMap(function () {this.fade(1, true);//beforeScrollStart还没有开始滚动});});
}//绑定refresh事件
this.on('refresh', function () {_indicatorsMap(function () {this.refresh();});
});
//绑定destroy事件
this.on('destroy', function () {_indicatorsMap(function () {this.destroy();});delete this.indicators;
});

从上面我们可以清楚的看到,我们为iScroll对象绑定了refresh事件

       //绑定refresh事件this.on('refresh', function () {_indicatorsMap(function () {this.refresh();});});

在refresh事件中我们调用了Indicators中的所有的refresh事件,我们先看看iScroll对象的refresh事件:

//刷新:refresh做的事情就是获取水平垂直可以滚动的距离,然后触发refresh事件refresh: function () {utils.getRect(this.wrapper);//首先获取到包裹元素矩形对象的clientWidth/clientHeight,clientWidth=width+2*borderWidththis.wrapperWidth  = this.wrapper.clientWidth;this.wrapperHeight  = this.wrapper.clientHeight;//获取scroller元素的矩形对象,也就是他的width/height属性var rect = utils.getRect(this.scroller);this.scrollerWidth  = rect.width;this.scrollerHeight   = rect.height;//maxScrollX,maxScrollY表示的最大的滚动距离,其值为父元素的clientWidth-子元素的width//wrapper可以设置width,但是scroll是不可以设置宽度的,所以maxScrollX如果为负数,那么表示scroll特别宽,这时候表示可以往左边移动,也就是是负数//wrapper可以设置height,但是scroll是不可以设置高度的,所以maxScrollY如果为负数,表示元素可以往上面移动this.maxScrollX      = this.wrapperWidth - this.scrollerWidth;this.maxScrollY       = this.wrapperHeight - this.scrollerHeight;this.hasHorizontalScroll    = this.options.scrollX && this.maxScrollX < 0;this.hasVerticalScroll        = this.options.scrollY && this.maxScrollY < 0;//如果指定了scrollX,同时maxScrollX<0。那么这时候表示有水平的滚动条,如果>=0肯定是没有水平滚动条的//如果指定了scrollY,同时maxScrollY<0。那么这时候表示有垂直的滚动条,如果>=0肯定是没有垂直滚动条的if ( !this.hasHorizontalScroll ) {this.maxScrollX = 0;this.scrollerWidth = this.wrapperWidth;}//如果没有垂直滚动条,那么maxScrollY就是0,同时scroll的高度和wrap的高度是一样的if ( !this.hasVerticalScroll ) {this.maxScrollY = 0;this.scrollerHeight = this.wrapperHeight;}this.endTime = 0;this.directionX = 0;this.directionY = 0;this.wrapperOffset = utils.offset(this.wrapper);//获取wrapper元素的offset值,一直往上计算,一直到该元素没有offsetParent为止,同时要记住:这是逐级往上计算的,而且这是负数,通过这种方式可以简单的获取到距离document的距离!this._execEvent('refresh');//触发refresh事件this.resetPosition();}

在iScroll对象的refresh事件中主要是获取到可以滚动的垂直方向和水平方向的距离,同时触发iScroll对象的refresh事件。在看iScroll的refresh事件之前我们首先看看resetPosition方法:

//重新设置位置,以time作为参数//that.resetPosition(that.options.bounceTime)resetPosition: function (time) {//this.x、this.y表示iScroll对象当前所在的位置var x = this.x,y = this.y;time = time || 0;//如果没有水平滚动条或者this.x>0那么x=0if ( !this.hasHorizontalScroll || this.x > 0 ) {x = 0;//如果this.x<this.maxScrollX那么水平方法可以滚动的距离为this.maxScrollX } else if ( this.x < this.maxScrollX ) {x = this.maxScrollX;}//没有垂直滚动条y=0,如果有垂直滚动条那么就是this.maxScrollYif ( !this.hasVerticalScroll || this.y > 0 ) {y = 0;} else if ( this.y < this.maxScrollY ) {y = this.maxScrollY;}if ( x == this.x && y == this.y ) {return false;}//滚动到x,y的坐标,时间为time,函数为this.options.bounceEasing。调用对象为该iScroll对象this.scrollTo(x, y, time, this.options.bounceEasing);return true;}

看完resetPosition,我们看看触发了iScroll的refresh事件时候,iScroll是如何处理的:

this.on('refresh', function () {_indicatorsMap(function () {this.refresh();});});

很显然,其会触发所有的Indicator的refresh事件:

参考文献:

从源码角度深入理解iScroll中的scrollbars和indicators配置相关推荐

  1. 从源码角度深入理解Toast

    Toast这个东西我们在开发中经常用到,使用也很简单,一行代码就能搞定: 1: Toast.makeText(this, "333", Toast.LENGTH_LONG).sho ...

  2. 从源码角度解析线程池中顶层接口和抽象类

    摘要:我们就来看看线程池中那些非常重要的接口和抽象类,深度分析下线程池中是如何将抽象这一思想运用的淋漓尽致的. 本文分享自华为云社区<[高并发]深度解析线程池中那些重要的顶层接口和抽象类> ...

  3. 从源码角度彻底理解ReentrantLock(重入锁)

    源码分析ReentrantLock https://blog.csdn.net/lxltmac/article/details/84871929白话讲解lock

  4. [Java]源码角度深入理解哈希表,手撕常见面试题

    专栏简介 :java语法及数据结构 题目来源:leetcode,牛客,剑指offer 创作目标:从java语法角度实现底层相关数据结构,达到手撕各类题目的水平. 希望在提升自己的同时,帮助他人,,与大 ...

  5. 从源码角度解读 xml 文件中的 xmlns、xsi、xsd

    xml 文件中的 xmlns.xsi.xsd xmlns xsi xsd 下面是 spring.xml 中的一段: <beans xmlns="http://www.springfra ...

  6. 从源码角度理解 FragmentTransaction实现

    谈到fragment的使用,肯定绕不过FragmentTransaction事务,对fragment的操作必定用到它,其提供show,hide,add,remove,replace等常用的fragme ...

  7. 从源码角度理解LinearLayout#onMeasure对child的measure调用次数

    熟悉绘制流程的都知道,ViewGroup可以决定child的绘制时机以及调用次数. 今天我们就从LinearLayout开始学起,看一下它对子View的onMeasure调用次数具体是多少. 简单起见 ...

  8. 从源码角度理解FrameLayout#onMeasure对child的measure调用次数

    熟悉绘制流程的都知道,ViewGroup可以决定child的绘制时机以及调用次数. 今天我们就从最简单的FrameLayout开始学起,看一下它对子View的onMeasure调用次数具体是多少. 简 ...

  9. 从源码角度理解ConstraintLayout#onMeasure对child的measure调用次数

    熟悉绘制流程的都知道,ViewGroup可以决定child的绘制时机以及调用次数. 今天我们简单看下较为复杂的ConstraintLayout,看一下它对子View的onMeasure调用次数具体是多 ...

  10. 从源码角度解析Android中APK安装过程

    从源码角度解析Android中APK的安装过程 1. Android中APK简介 Android应用Apk的安装有如下四种方式: 1.1 系统应用安装 没有安装界面,在开机时自动完成 1.2 网络下载 ...

最新文章

  1. notepad++安装
  2. MySQL子查询操作实例详解
  3. 安卓应用自动化测试工具汇总
  4. Python+Opencv根据颜色进行目标检测
  5. sharepoint timer job 读取config文件内容
  6. MongoDB数据库可视化工具实现删除功能
  7. mongodb集群分片环境搭建
  8. python二次开发ug_CAD二次开发(UG/Proe/其他) - 随笔分类 - 白途思 - 博客园
  9. 苹果连接电脑 计算机不显示硬盘,苹果连接电脑没反应怎么办?苹果连接电脑没反应解决方法...
  10. android 4g ram够么,4G还不够,安卓手机内存极限是多少
  11. 天蝎座最适合的职业-天蝎座不同型血适合工作分析
  12. gif动态图太大如何发微信?手机如何快速压缩动图?
  13. JustSoso笔记
  14. 启天m420进入不了bios_联想启天M420c装win7及BIOS设置教程(USB驱动可用)
  15. [千峰安全篇9]Public Key Infrastructure
  16. 这可能是前端开发中能遇到最全的cookie问题了
  17. 【逻辑漏洞】业务中常见的漏洞
  18. Appium自动化测试框架
  19. .net 多线程之线程取消
  20. Apollo自动驾驶进阶课(5)——Apollo感知技术

热门文章

  1. 从微盟员工删库跑路看程序员的职业素养。
  2. 安徽汽车网程序员删库跑路?安徽汽车官网只剩3张图片!
  3. c语言if用法详解,C语言if语句的使用讲解
  4. 利用jsonp跨域访问
  5. 顺利通过2020年下工信部的系统架构设计师考试,在此感悟一下
  6. 「HEOI 2014」南园满地堆轻絮
  7. Cadence用于版图设计时芯片logo的制作
  8. python爬取去哪儿网机票_去哪儿网机票爬虫
  9. javascript——构造函数和原型对象
  10. Nik Collection v3.0.7 2020 Mac/Win PS/LR超强调色滤镜合集Nik插件中文版+中文教程