IOS端 vux中scroll滚动自动回弹到顶部或者左侧的解决办法

IOS端 vux中scroll滚动自动回弹到顶部或者左侧的解决办法

问题表象

在滑动的时候,偶发性出现自动回弹到顶部的现象,通过onScroll回调发现scrollTop为-1造成回弹,开始寻找为什么scrollTop为-1;

排查问题

1.官网查看例子,发现例子也有,初步断定并不是业务代码的问题
2.查看node_modules文件中的vux的scroll组件
3.发现引用的是node_modules/vux-xscroll组件,找到对应组件

//...
import XScroll from 'vux-xscroll/build/cmd/xscroll.js'
//...

4.找到SimuScroll组件,分析代码,重点查看手势结束事件,因为是松开手指才滚动回去的,最终使用的是SimuScroll库,一个原生滚动,一个是模拟的,
原生滚动应该说的overflow:auto的滚动条,模拟的那个是利用手势加上css3的transform来实现的。

//...
var Util = require('./util'),
	Base = require('./base'),
	Timer = require('./timer'),
	Animate = require('./animate'),
	Hammer = require('./hammer'),
	SimuScroll = require('./simulate-scroll'),
	OriginScroll = require('./origin-scroll');
var XScroll = function(cfg) {
        //最终使用的是SimuScroll库,一个原生滚动,一个是模拟的
		var _ = cfg && cfg.useOriginScroll ? OriginScroll : SimuScroll;
		return new _(cfg);
	}
//...

5.关注boundryCheckY函数,因为是滚动完才触发的回弹,估计是边界检测出的问题


function _onpanend(e) {
    var self = this;
    var userConfig = self.userConfig;
    var transX = self.computeScroll("x", e.velocityX);
    var transY = self.computeScroll("y", e.velocityY);
    var scrollLeft = transX ? transX.pos : 0;
    var scrollTop = transY ? transY.pos : 0;
    var duration;
    if (transX && transY && transX.status == "inside" && transY.status == "inside" && transX.duration && transY.duration) {
      //ensure the same duration
      duration = Math.max(transX.duration, transY.duration);
    }
    transX && self.scrollLeft(scrollLeft, duration || transX.duration, transX.easing, function(e) {
      self.boundryCheckX();//重点关注对象
    });
    transY && self.scrollTop(scrollTop, duration || transY.duration, transY.easing, function(e) {
      self.boundryCheckY();//重点关注对象
    });
    //judge the direction
    self.directionX = e.velocityX < 0 ? "left" : "right";
    self.directionY = e.velocityY < 0 ? "up" : "down";
    //clear start
    self.__topstart = null;
    self.__leftstart = null;
    return self;
  }

6.关注isBoundryOutTop和isBoundryOutBottom函数,一个是判断顶部是否越界,一个是顶部是否越界

function boundryCheckY(duration, easing, callback) {
    var self = this;
    if (!self.userConfig.boundryCheck) return;
    if (typeof arguments[0] == "function") {
      callback = arguments[0];
      duration = self.userConfig.BOUNDRY_CHECK_DURATION;
      easing = self.userConfig.BOUNDRY_CHECK_EASING;
    } else {
      duration = duration === 0 ? 0 : self.userConfig.BOUNDRY_CHECK_DURATION,
        easing = easing || self.userConfig.BOUNDRY_CHECK_EASING;
    }
    if (!self.userConfig.boundryCheck || self.userConfig.lockY) return;
    var boundry = self.boundry;
    if (self.isBoundryOutTop()) {
      self.scrollTop(-boundry.top, duration, easing, callback);
    } else if (self.isBoundryOutBottom()) {
      self.scrollTop(self.containerHeight - boundry.bottom, duration, easing, callback);
    }
    return self;
  }

7.isBoundryOutTop调用的getBoundryOutTop,getBoundryOutTop调用了getScrollTop,所有只需要看getScrollTop函数即可

function isBoundryOutTop() {
    return this.getBoundryOutTop() > 0 ? true : false;
  }
function getBoundryOutTop() {
  return -this.boundry.top - this.getScrollTop();
}
function getScrollTop() {
  var transY = window.getComputedStyle(this.container)[transform].match(/[-\d\.*\d*]+/g);
  //alert(window.getComputedStyle(this.container)[transform])
  //alert(transY[5])
  //alert(transY)
  return transY ? Math.round(transY[5]) === 0 ? 0 : -Math.round(transY[5]) : 0;
}

8.alert上面三个关键值,发现是正则写错了,作者是想把数字分割,但没有考虑到科学计数法写法

//
console.log("0,1,2".match(/[-\d\.*\d*]+/g));// ["0", "1", "2"]
console.log("1e100,-1.1e-17,0,1,2".match(/[-\d\.*\d*]+/g));//["1", "100", "-1.1", "-17", "0", "1", "2"]

解决方案

/[-\d\.*\d*]+/g 改为 /[-\d.+e]+/g
优化了一下:中括号内点不用转移,*号并不作为一个数字该有的符合,\d出现了两次可以去掉一个,增加了e和正号

并不能直接更改里面的代码,虽然作者已经不维护了,可以在main.js或者其他初始化的地方增加以下代码(这代码不行,生产翻车了)

try {
    const scrollPrototype = require('vux-xscroll/build/cmd/simulate-scroll.js').prototype;
    const Util = require('vux-xscroll/build/cmd/util.js');
    const transform = Util.prefixStyle("transform");
    const fnGetScrollTopStr = scrollPrototype.getScrollTop.toString();
    const fnGetScrollLeftStr = scrollPrototype.getScrollLeft.toString();
    scrollPrototype.getScrollTop=function () {
        //此代码有问题,上版本后翻车了,大家引以为鉴
        return new Function("transform","scrollPrototype","return ("+fnGetScrollTopStr.replace(/\*\\d\*/,"+e")+").call(this)").call(this,transform)
    };
    scrollPrototype.getScrollLeft=function () {
        return new Function("transform","scrollPrototype","return ("+fnGetScrollLeftStr.replace(/\*\\d\*/,"+e")+").call(this)").call(this,transform)
    };
}catch (e) {

}

new Function翻车了,测试环境没一点问题,上了生产就凉了,代码会报错,原因是测试环境并不压缩代码中的变量定义,你定义的transform还是transform
但是生产环境可能就压缩成了单个字母,所以new Function不可取,建议还是常规操作即可

try {
    const scrollPrototype = require('vux-xscroll/build/cmd/simulate-scroll.js').prototype;
    const Util = require('vux-xscroll/build/cmd/util.js');
    const transform = Util.prefixStyle("transform");
    scrollPrototype.getScrollTop=function () {
        var transY = window.getComputedStyle(this.container)[transform].match(/[-\d.+e]+/g);
        return transY ? Math.round(transY[5]) === 0 ? 0 : -Math.round(transY[5]) : 0;
    };
    scrollPrototype.getScrollLeft=function () {
        var transX = window.getComputedStyle(this.content)[transform].match(/[-\d.+e]+/g);
        return transX ? Math.round(transX[4]) === 0 ? 0 : -Math.round(transX[4]) : 0;
    };
}catch (e) {

}

问题解决

总结

正则内容挺多,还要考虑到贪婪匹配的性能问题,用的好很便利,细节没考虑到就翻车了。

关于科学计数法,可以参考我的另外的一篇文章
parseInt不应该用作 Math.floor的替代品 parseInt脱坑指南

感谢阅读

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值