移动端滚动穿透的解决方案

一.在滚动区域先下滑再上滑导致无法滚动

1.情景复现

当弹出层内容可以滚动时,如果我们直接往上滑,是可以正常触发滚动的,但是如果当内容已经滚动到顶部时,我们先往下滑,再往上滑就无法触发内容滚动了。

2.问题分析

这个问题我把它归到滚动穿透的底层可以滚动,弹出层可以滚动一类。其原因就是当弹出层无法滚动时,滚动时间会想外层dom传递,也就是被底层给捕获率,从而引起底层滚动。我们通常的解决方案是给底层加上overflow:hidden和position:fixed去禁止底层滚动,但这种方法并不能解决上面的问题,而且会导致顶层可滚动区域回到顶部。下面我介绍的这种方案可以完美解决滚动穿透和无法滚动的问题,同时也不会导致底层内容回到顶部。

3.方案原理

监听touchmove事件,判断开始滑动时是否在顶部,若是则阻止默认事件,方式滚动传递出去,若不是,则不作操作。
需要做的事情有:
1、预存一个全局变量targetY
2、监听可滚动区域的touchstart事件,记录下第一次按下时的
e.targetTouches[0].clientY值,赋值给targetY
3、后期touchmove里边获取每次的e.targetTouches[0].clientY与第一次的进行比较,可以得出用户是上滑还是下滑手势。
4、如果手势是向上滑,且页面现在滚动的位置刚好是整个可滚动高度——弹窗内容可视区域高度的值,说明上滑到底,阻止默认事件。
同理,如果手势是向下滑,并且当前滚动高度为0说明当前展示的已经在可滚动内容的顶部了,此时再次阻止默认事件即可。

4.代码案例

//弹出层滚动区域容器

{{item.question}}

data(){ return { targetLeftY: 0, } }, methods:{ leftTouchStart(e) { this.targetLeftY = Math.floor(e.targetTouches[0].clientY); }, leftTouchMove(e) { let newTargetY = Math.floor(e.targetTouches[0].clientY); let dom = document.getElementById('des-container-wrapper'); let sT = dom.scrollTop; let sH = dom.scrollHeight; let cH = dom.clientHeight; if (sT <= 0 && newTargetY - this.targetLeftY > 0) { e.preventDefault(); } else if ((sT >= sH - cH) && newTargetY - this.targetLeftY < 0) { e.preventDefault(); } }, },

二.其他常见方案

1.解决方案:

弹层出现时,用css给body设置固定定位和超出隐藏。
至于弹层内部的滚动,设置一个overflow: scroll;即可。
不过为了流畅体验,可以加上-webkit-overflow-scrolling: touch,以解决在IOS上滚动惯性失效的问题,提高滚动的流畅度。

        //弹窗显示
        document.body.style.overflow = 'hidden'
        document.body.style.position = 'fixed'
        //弹窗隐藏
        document.body.style.overflow = 'auto'
        document.body.style.position = 'static'
        //弹窗滚动容器样式
        overflow-y: scroll
        -webkit-overflow-scrolling: touch

2.局限问题:

若用户在body层滚动了很长的距离再打开弹出层,这种方案会导致body层回到顶部,那么就需要在打开弹出层之前记录body层的位置,在关闭弹出层之后将body层位置还原。弹层中内容滚动到顶部或底部后,还会连带页面body一起滚动。也就是还会发生穿透效果。

你可能感兴趣的:(移动端滚动穿透的解决方案)