移动端滑动穿透方案及demo

移动端滑动穿透方案及demo

移动端滑动穿透问题通常出现在弹框出现时,上下滑动屏幕,背景也跟着滑动了

解决的办法有:
1、 加overflow:hidden

html, body {
   overflow: hidden;
}

(在移动端无效)

2、 弹窗出现时将背景设置成fixed,并记录坐标,关闭时恢复位置
(背景位置会有个跳动)

3、阻止body的默认滚动?

document.ontouchmove = e => {
	e.preventDefault();
};

(该方案好像在Android中不生效?)

下面就针对第三种方法进行优化

参考文章 龙喵 张鑫旭

我们监听 touchmove事件时,在之前是有一个小延迟触发的,因为浏览器不知道我们是否要 preventDefault,所以等到大概200ms左右才能真正收到监听回调。

chrome在56版本将 addEventListner默认的 passive置为true,具体请参见这里,这样浏览器就能知道这个 addEventListner是不用 preventDefault的,立即可触发滚动事件。

在Android的手q和微信中使用的是X5内核,它是基于blink内核的,因此同样有关于 passiveevent的优化。所以我们需要加入 addEventListner的第三个参数:

document.addEventListener(
	'touchmove',
	e => {
		e.preventDefault();
	},
	{ passive: false },
);

这样就解决了android不生效的问题,不过此时所有滑动都被禁止了,即使弹框中有超出内容,我们需要进行滑动查看。

那么,我们需要增加一个可以滚动的元素:

document.addEventListener(
  'touchmove',
  e => {
	const excludeEl = document.querySelectorAll('.can-scroll');
	const isExclude = [].some.call(excludeEl, (el: HTMLElement) =>
	  el.contains(e.target),
	);
	if (isExclude) {
	  return true;
	}
	e.preventDefault();
},
{ passive: false },

如此就解决了内容滑动的问题,可是,当滑动超出了边界时,又会触发默认的滚动穿透,这时我们又需要添加边界条件来阻止滚动:

elScroll = e.target.offsetParent || e.target
// 当前的滚动高度
scrollTop = elScroll.scrollTop;
events = e.touches[0] || e;
distanceY = events.pageY - posY;
//上下边缘检测
distanceY > 0 && scrollTop == 0
//下边缘检测
distanceY < 0 && scrollTop + 1 >= maxscroll

后来又发现要是有多个可滑动的弹框,又出现滚动穿透的问题

so, 直接上demo:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    html {
      touch-action: none;
    }
    .mask {
      position: fixed;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      background: rgba(0, 0, 0, 0.4);
    }
    .can-scroll {
      height: 200px;
      overflow: auto;
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: 50%;
      border-radius: 8px;
      background: #fff;
      -webkit-overflow-scrolling: touch;
    }
  </style>
</head>

<body>
  <div>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
    <p>background</p>
  </div>
  <div class="mask">
    <div class="can-scroll">
      <p>content</p>
      <p>content</p>
      <p>content</p>
      <p>content</p>
      <p>content</p>
      <p>content</p>
      <p>content</p>
      <p>content</p>
      <p>content</p>
      <p>content</p>
    </div>
  </div>

  <script>
    let posY = '';
    let maxscroll = '';
    let scrollTop = "";
    document.addEventListener("touchstart", eventStart, { passive: false });
    document.addEventListener("touchmove", eventMove, { passive: false });
    function eventMove(e) {
      const excludeEl = document.querySelectorAll(".can-scroll");
      const isExclude = [].some.call(excludeEl, el => el.contains(e.target));
      if (isExclude) {
        let elScroll = e.target.offsetParent || e.target
        // 当前的滚动高度
        scrollTop = elScroll.scrollTop;
        // 现在移动的垂直位置,用来判断是往上移动还是往下
        let events = e.touches[0] || e;
        // 移动距离
        let distanceY = events.pageY - posY;
        // 上下边缘检测
        if (distanceY > 0 && scrollTop == 0) {
          // 往上滑,并且到头
          // 禁止滚动的默认行为
          e.preventDefault();
          return;
        }
        // 下边缘检测
        if (distanceY < 0 && scrollTop + 1 >= maxscroll) {
          // 往下滑,并且到头
          // 禁止滚动的默认行为
          e.preventDefault();
          return;
        }
        return true;
      }
      e.preventDefault();
    }
    function eventStart(e) {
      let elScroll = e.target.offsetParent || e.target
      let events = e.touches[0] || e;
      posY = events.pageY;
      maxscroll = elScroll.scrollHeight - elScroll.clientHeight;
      scrollTop = elScroll.scrollTop;
    }
  </script>
</body>

上面两篇参考文章写的非常好,推荐!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值