谨慎设置iScroll4的useTransform属性,他会导致scrollToElement方法表现异常

问题描述:

  在创建iScroll实例的时候设置useTransform属性值为false,结果后面在使用scrollToElement方法滑动页面到指定位置时,除非滑动区域初始滑动距离为0,否则使用scrollToElement滑动时总会出现一定偏差,初始滑动距离越大,偏差越大。

原因分析:

  1、找到scrollToElement方法的定义: 

    scrollToElement: function (el, time) {
            var that = this, pos;
            el = el.nodeType ? el : that.scroller.querySelector(el);
            if (!el) return;

            pos = that._offset(el);
            pos.left += that.wrapperOffsetLeft;
            pos.top += that.wrapperOffsetTop;

            pos.left = pos.left > 0 ? 0 : pos.left < that.maxScrollX ? that.maxScrollX : pos.left;
            pos.top = pos.top > that.minScrollY ? that.minScrollY : pos.top < that.maxScrollY ? that.maxScrollY : pos.top;
            time = time === undefined ? m.max(m.abs(pos.left) * 2, m.abs(pos.top) * 2) : time;

            that.scrollTo(pos.left, pos.top, time);
        }

  调试可发现,出现问题时以上代码中that._offset(el)方法放回的数据有问题(pos.top值偏小);

   2、找到offset(el)方法定义

    _offset: function (el) {
            var left = -el.offsetLeft,
                top = -el.offsetTop;

            while (el = el.offsetParent) {
                left -= el.offsetLeft;
                top -= el.offsetTop;
            }

            if (el != this.wrapper) {
                left *= this.scale;
                top *= this.scale;
            }

            return {left: left, top: top};
        }

  问题就出在红色标记部分了,调试可发现top值多减去了滑动区域的offsetTop,这又是为什么呢?

  看一下offsetParent是什么意思:

offsetParent属性返回一个对象的引用,这个对象是距离调用offsetParent的元素最近的(在包含层次中最靠近的),并且是已进行过CSS定位的容器元素。 如果这个容器元素未进行CSS定位, 则offsetParent属性的取值为根元素(在标准兼容模式下为html元素;在怪异呈现模式下为body元素)的引用。 当容器元素的style.display 被设置为 "none"时(译注:IE和Opera除外),offsetParent属性 返回 null

  简单的说就是获取最近有设置position属性的父元素,但我们一般都不会给滑动区域设置position属性的,可通过调试发现滑动区域确实有设置position属性,这是为啥?于是想到了iScroll的构造函数。

  在iScroll4的构造函数中有一下代码,若useTransform为false,则会给滑动块(scroller)添加css属性position:absolute;

if (that.options.useTransform) that.scroller.style[transform] = 'translate(' + that.x + 'px,' + that.y + 'px)' + translateZ;
else that.scroller.style.cssText += ';position:absolute;top:' + that.y + 'px;left:' + that.x + 'px';

  至此终于真相大白!

  总结下:在创建iScroll4实例时若设置seTransform为false,则iScroll4会给可滑动区域设置position:absolute,导致在运行scrollToElement方法过程中调用_offset方法计算滑动距离时会把滑动区域的offsetTop值也计算进去,最终导致滑动距离小于预期。

解决方案:

  修改_offset定义,使其不计算滑动区域offset:

    _offset: function (el) {
            var left = -el.offsetLeft,
                top = -el.offsetTop;

            while (el = el.offsetParent) {
                if (el == this.scroller) continue;//若为滑动块,则跳过
                left -= el.offsetLeft;
                top -= el.offsetTop;
            }

            if (el != this.wrapper) {
                left *= this.scale;
                top *= this.scale;
            }

            return {left: left, top: top};
        }

 

posted @ 2015-08-12 12:21  庚午月圆人  阅读(2074)  评论(0编辑  收藏  举报