从源码角度深入理解iScroll中的snap选项的含义

问题1:官方解释是啥?

解答:snap值可以为true 或是 DOM元素的tagname,当为true 时,对齐的坐标会根据可滚动的位置和滚动区域计算得到可滑动几页。如果为tagname,则滑动会对齐到元素上。

通俗点,就是说应该滚动到第几屏幕。如果设置为true,那么就会通过下面的双重循环得到可以水平和垂直滚动的屏幕数,并计算得到每一个屏幕的位置坐标。如果设置为string,那么我们就只会通过简单的offset来得到元素的位置信息。因为如果scroller没有设置position那么iScroll会手动设置为relative,所以定位都是相对于iscroll来说的!

问题2:为什么需要双重循环来得到this.pages集合

while ( x > -this.scrollerWidth ) {
		//this.scrollerWidth表示的是滚动的scroller的宽度
		this.pages[i] = [];
		l = 0;
		y = 0;
        //this.scrollerHeight表示scroller的高度
		while ( y > -this.scrollerHeight ) {
			this.pages[i][l] = {
				x: Math.max(x, this.maxScrollX),
				//scroller至少为relative定位,此处x表示距离元素的水平距离,其会随着元素的屏幕切换而变化
				y: Math.max(y, this.maxScrollY),
				//scroller至少为relative定位,此处y表示距离元素的垂直距离,其会随着元素的屏幕切换而变化
				width: stepX,
				//wrapper元素的宽度
				height: stepY,
				//wrapper元素的高度
				cx: x - cx,
				//表示当前屏幕的中间x位置,只和宽度有关,0-896
				cy: y - cy
				//表示当前屏幕的中间y位置,只和高度有关,0-318;
			};
            //减去wrapper元素的高度,通过这种方式可以计算得到我们的元素有几个屏幕的高度,
            //因为每次y的值都会减小其wrapper的高度
			y -= stepY;
			//对于y来说,一开始为0,后面为-1*(wrapperHeight).....-n*(wrapperHeight)
			//所以,我们就会得出结论:cx,cy表示的是当前页面的中心位置,向下的方向为负值!!!
			l++;
		}
        //水平方向计算可以有几个屏幕的宽度的距离
		x -= stepX;
		i++;
	}
通过这个双重循环,你就可以知道每一屏幕的元素的距离的坐标是什么,如下面所示:

 /*this.pages表示的是如下的格式:
 [
	 [{cx:-896,cy:-318,height:635,width:1792,x:0,y:0},
	 {cx:-896,cy:-953,height:635,width:1792,x:0,y:-635},
	 {cx:-896,cy:-2223,height:635,width:1792,x:0,y:-1270},
	 {cx:-896,cy:-318,height:635,width:1792,x:0,y:-1365}]
 ]
 其中this.wrapperWidth=1792px
 其中this.wrapperHeight=635px
 */
这是只有垂直滚动的demo得到的this.pages集合,你可以清楚的看到,数组的第一个元素也是一个数组,该数组有4个元素。而第一个元素的下标就是表示的第一列,而四个元素表示的就是第一列具有的多少行,也就是可以垂直方向滚动的行数!通过上面的集合,你也可以看到cy,y一直在变动,而cx和x都没有变动! 如果有多列(也就是在水平上也可以滚动)的情况下,上面的数组样式应该很容易理解
问题3:如果不是把snap设置为true,而是字符串,这时候该如何得到上面的pages集合

/*如果配置的不是布尔值,而是一个选择器,那么this.options.snap就是scroller下某个选择器选中的元素的集合*/
	el = this.options.snap;
	l = el.length;
	n = -1;
	//其中m和n表示的多少行多少列,和上面的对比后知道第一个m表示的行,第二个n表示的是列
	//因为上面的是i表示的是第几行,而l表示的是第几列,所以bounce-easing的demo才会得到上面的结构
	//数组中只有一个元素,该元素还是一个数组,这个数组元素中是四个Object对象!
	for ( ; i < l; i++ ) {
		//如果是第一个元素,或者元素的offsetLeft没有变化,那么表示是垂直方向上的变化
		//这时候修改行就可以了,列都是第一列,也就是是0!这样就可以计算出元素可以分为多少列了
		if ( i === 0 || el[i].offsetLeft <= el[i-1].offsetLeft ) {
			m = 0;
			n++;
		}
        //设置为一个空数组
		if ( !this.pages[m] ) {
			this.pages[m] = [];
		}
		x = Math.max(-el[i].offsetLeft, this.maxScrollX);
		//第i个元素的x方向距离,取其和水平方向滚动距离的最大值
		y = Math.max(-el[i].offsetTop, this.maxScrollY);
		//第i的元素的y方向的距离,取其和垂直方向滚动距离的最大值
		cx = x - Math.round(el[i].offsetWidth / 2);
		cy = y - Math.round(el[i].offsetHeight / 2);
        //cx,cy表示的是当前所在页面的中心位置,因为所有的元素都是相对于scroller进行定位的
        //所以中心就是:元素本身相对于scroller的偏移量减去元素本身的宽度和高度
		this.pages[m][n] = {
			x: x,//元素的offsetLeft
			y: y,//元素的offsetTop
			width: el[i].offsetWidth,//元素的宽度
			height: el[i].offsetHeight,//元素的高度
			cx: cx,//元素中心相对于scroller移动的距离,也就是元素的中心位置
			cy: cy//元素中心相对于scroller移动的距离,也就是元素的中心位置
		};
        //如果元素的offsetLeft大于可以滚动的距离,那么就是横向加1
		if ( x > this.maxScrollX ) {
	    //其中this.maxScrollX= this.wrapperWidth - this.scrollerWidth;
	    //表示的是包裹元素的宽度(一般是固定的)和滚动元素的宽度的差值
			m++;
		}
	}
}
通过上面的分析,这里已经不难理解了。

if ( i === 0 || el[i].offsetLeft <= el[i-1].offsetLeft ) {
	m = 0;
	n++;
}
如果是水平方向的滚动,那么后面的元素肯定不会小于前面的offsetLeft,所以这里不会执行。只会在i==0的时候执行一次。而会执行下面的逻辑:

if ( x > this.maxScrollX ) {
	m++;
}
因为此时的x表示元素本身距离scroller的距离,所以m自增,因此变换的是列,这样的话就会成为水平滚动。如果是垂直滚动的话,那么就会走上面的if逻辑,所以变换的是列!
问题4:snapThresholdX,snapThresholdY表示的是什么?

if ( this.options.snapThreshold % 1 === 0 ) {
		this.snapThresholdX = this.options.snapThreshold;
		this.snapThresholdY = this.options.snapThreshold;
	} else {
		//this.pages[this.currentPage.pageX][this.currentPage.pageY].width获取的是某一行某一列的元素的宽度,所以
		//snapThresholdX和snapThresholdY表示的是滑动的时候临界距离
		this.snapThresholdX = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].width * this.options.snapThreshold);
		this.snapThresholdY = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].height * this.options.snapThreshold);
	}
所以通过这里你就会清楚的知道,其表示的是在当前屏幕中滑动时候响应事件的临界值!
问题5:flick时候我们会切换响应的屏幕展示内容

//为iScroll添加一个flick事件
this.on('flick', function () {
	var time = this.options.snapSpeed || Math.max(
			Math.max(
				Math.min(Math.abs(this.x - this.startX), 1000),
				Math.min(Math.abs(this.y - this.startY), 1000)
			), 300);
    //跳转到特定的页面
	this.goToPage(
		this.currentPage.pageX + this.directionX,
		//directionX用于计算下一页,this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
		this.currentPage.pageY + this.directionY,
		time
	);
});

总结:通过上面的分析你应该知道snap设置了时候,如果设置为了true那么就会计算当前可以在水平和垂直方向切换的屏幕数以及元素在该屏幕中的具体位置和尺寸,会自动通过wrapper的width/height计算;而如果设置为string,那么就会通过每一个集合元素的offsetLeft来获取每一个屏幕中元素的位置尺寸信息

参考文献:

IScroll 实践指南(中)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值