Vue解决移动端弹窗滚动穿透问题


Posted in Vue.js onDecember 15, 2020

一、问题描述

  1. 在移动端的H5页面中,我们经常会遇到 “点击按钮-->弹窗-->选择选项” 这样的场景。而在选项过多出现滚动条时,滚动滚动条至容器的底部或者顶部。再往上或往下拖动滚动条时,滚动动作会出现穿透,这时候底部的body也会一起滚动。
  2. 问题总结:内容在滚动到容器的顶部或者底部时,再向上或向下 强行滚动 ,则出现滚动穿透

Vue解决移动端弹窗滚动穿透问题

二、解决方案思考

参考了网上一大堆的解决方法,大可分为三类方法。经过认真的思考和分析,个人的总结如下:

  • 使用js去控制和改变css
1. 弹窗出现
	1.1. 记录点击出现弹窗按钮位置的scrollTop
	1.2. 给body样式{'overflow': 'hidden'}
2. 弹窗关闭
	2.1. 取消body样式{'overflow': 'hidden'}
	2.2. 给body样式{'top': scrollTop}
  
优点:实现简单快捷
缺点:在弹窗一开一关的时间段,如果弹窗不是沾满整个窗口,则会看到body闪烁
  • 使用js去控制弹窗内容区的默认滚动事件
1. 弹窗出现
  1.1. 监听内容容器layoutBox的touchstart和touchmove事件
  1.2. 监听touchstart事件,得知手指开始滚动内容区的起始位置targetY
  1.3. 监听touchmove事件,得知滚动内容区的过程中,变化的位置newTargetY
  1.4. 拿到 内容滚动到容器顶部的距离 scrollTop / 内容可滚动的高度 scrollHeight / 当前容器的高度 clientHeight
  1.5. 在滚动到顶部和滚动到底部时,阻止内容容器的默认行为。(关键点)
2. 弹窗正常关闭

优点:从出现滚动穿透问题的源头出发,把问题解决,js实现不存在ios兼容问题
缺点:实机验证,个别品牌的机型存在兼容性问题
  • 弹窗内容区禁止滚动,使用js模拟滚动条
1. 弹窗出现 
 1.1. 监听touchmove事件,全程阻止默认行为 
 1.2. 监听touchstart和touchmove事件记录手指的移动距离,使用transform: translate3d()属性,实现内容滚动 
2. 弹窗正常关闭

优点:js实现不存在ios兼容问题
缺点:ios上丢失了原生滚动条的回弹体验

三、初步实现

写成一个mixin

/**
 * @author cunhang_wei
 * @description 解决弹窗内容区滚动穿透到body的问题
 * @param $refs.layoutBox 需要事先指定 内容容器
 */

export default {
  data () {
    return {
      targetY: 0
    }
  },

  mounted () {
    if (this.$refs.layoutBox) {
      this.$el.addEventListener('touchstart', this.handleTouchstart)
      this.$el.addEventListener('touchmove', this.handleTouchmove, false)
    }
    
  },

  methods: {
    handleTouchstart (e) {
      this.targetY = Math.floor(e.targetTouches[0].clientY) // 手指起始触摸位置
      console.log('handleTouchstart', this.targetY)
    },
    handleTouchmove (e) {
      let layoutBox = this.$refs.layoutBox // 内容容器
      let newTargetY = Math.floor(e.targetTouches[0].clientY) // 手指滑动中触摸位置
      let sTop = layoutBox.scrollTop // 内容滚动到容器顶部的高度
      let sHeight = layoutBox.scrollHeight // 内容的可滚动高度
      let cliHeight = layoutBox.clientHeight // 当前内容容器的高度
      if (sTop <= 0 && newTargetY - this.targetY > 0 && e.cancelable) {
        console.log('下拉到页面顶部')
        e.preventDefault()
      } else if (sTop >= sHeight - cliHeight && newTargetY - this.targetY < 0 && e.cancelable) {
        console.log('上翻到页面底部')
        e.preventDefault()
      }
    }
  },
  
  beforeDestroy () {
    if (this.$refs.layoutBox) {
      this.$el.removeEventListener('touchstart', this.handleTouchstart)
      this.$el.removeEventListener('touchmove', this.handleTouchmove)  
    }
  }
}

写完后发现每一次控制弹窗的滚动穿透,都需要引入这个 mixin 文件,未免有些累赘,查看了Vue的官方文档,发现了一种更好的办法,那就是 全局指令

四、写法优化

写成一个全局指令 no-through

/**
 * @author cunhang_wei
 * @description 解决弹窗内容区滚动穿透到body的问题(覆盖率90%)
 **/

// @description 用法
// <ul v-no-through>
//  <li></li>
//  <li></li>
//</ul>

// 使用vuex的保存一个全局变量
import state from 'src/vuex/modules/home/state'
export default {
  name: 'no-through',

  bind: function (el, binding) {
    const handleTouchstart = function (event) {
      state.targetY = Math.floor(event.targetTouches[0].clientY) // 手指起始触摸位置
    }
    const handleTouchmove = function (event) {
      let newTargetY = Math.floor(event.targetTouches[0].clientY) // 手指滑动中触摸位置
      let sTop = el.scrollTop // 内容滚动到容器顶部的高度
      let sHeight = el.scrollHeight // 内容的可滚动高度
      let cliHeight = el.clientHeight // 当前内容容器的高度
      if (sTop <= 0 && newTargetY - state.targetY > 0 && event.cancelable) {
        console.log('下拉到页面顶部')
        event.preventDefault()
      } else if (sTop >= sHeight - cliHeight && newTargetY - state.targetY < 0 && event.cancelable) {
        console.log('上翻到页面底部')
        event.preventDefault()
      }
    }
    el.addEventListener('touchstart', handleTouchstart)
    el.addEventListener('touchmove', handleTouchmove, false)
  },

  unbind: function (el, binding) {
    // 重置全局变量 targetY
    state.targetY = 0
  }
}

// 最后再去 main.js 注册为全局指令,即可使用。

实机测试

  • ios 测试通过 ios13
  • 小米、红米手机 测试通过 安卓10
  • 一加手机 测试通过 安卓10
  • 华为手机测试通过 emui11 安卓10
  • 三星S8上存在兼容问题 (初略估计和 Samsung webView的底层实现有关)

总结

  1. 解决问题关键在于:要清楚的知道 什么情况下才会发生滚动穿透
  2. 写法的优化,可以简洁代码,让代码更优雅

以上就是Vue解决移动端弹窗滚动穿透问题的详细内容,更多关于vue 解决弹窗滚动穿透的资料请关注三水点靠木其它相关文章!

Vue.js 相关文章推荐
vue实现购物车的小练习
Dec 21 Vue.js
vue中activated的用法
Jan 03 Vue.js
如何在 Vue 表单中处理图片
Jan 26 Vue.js
Vue实现todo应用的示例
Feb 20 Vue.js
vue常用高阶函数及综合实例
Feb 25 Vue.js
vue3.0中使用element的完整步骤
Mar 04 Vue.js
简单聊聊Vue中的计算属性和属性侦听
Oct 05 Vue.js
Vue提供的三种调试方式你知道吗
Jan 18 Vue.js
浅谈Vue的computed计算属性
Mar 21 Vue.js
一起来看看Vue的核心原理剖析
Mar 24 Vue.js
VUE解决跨域问题Access to XMLHttpRequest at
May 06 Vue.js
vue postcss-px2rem 自适应布局
May 15 Vue.js
8个非常实用的Vue自定义指令
Dec 15 #Vue.js
vue从后台渲染文章列表以及根据id跳转文章详情详解
Dec 14 #Vue.js
Vue在H5 项目中使用融云进行实时个人单聊通讯
Dec 14 #Vue.js
vue的hash值原理也是table切换实例代码
Dec 14 #Vue.js
Vue如何跨组件传递Slot的实现
Dec 14 #Vue.js
VUE中鼠标滚轮使div左右滚动的方法详解
Dec 14 #Vue.js
vue3.0实现插件封装
Dec 14 #Vue.js
You might like
php 在文件指定行插入数据的代码
2010/05/08 PHP
PHP高级对象构建 多个构造函数的使用
2012/02/05 PHP
PHP获取当前URL路径的处理方法(适用于多条件筛选列表)
2017/02/10 PHP
Javascript的一种模块模式
2010/09/08 Javascript
基于jQuery的让非HTML5浏览器支持placeholder属性的代码
2011/05/24 Javascript
JavaScript异步调用定时方法并停止该方法实现代码
2012/03/16 Javascript
js清空form表单中的内容示例
2014/05/20 Javascript
JS实现简单的顶部定时关闭层效果
2014/06/15 Javascript
jQuery简单实现隐藏以及显示特效
2015/02/26 Javascript
JavaScript日期类型的一些用法介绍
2015/03/02 Javascript
javascript实现详细时间提醒信息效果的方法
2015/03/11 Javascript
举例讲解JavaScript substring()的使用方法
2015/11/09 Javascript
javascript图片预加载完整实例
2015/12/10 Javascript
15个值得开发人员关注的jQuery开发技巧和心得总结【经典收藏】
2016/05/25 Javascript
js中 计算两个日期间的工作日的简单实例
2016/08/08 Javascript
javascript中Date对象的使用总结
2016/11/21 Javascript
JS字符串长度判断,超出进行自动截取的实例(支持中文)
2017/03/06 Javascript
老生常谈js数据类型
2017/08/03 Javascript
详解Webpack实战之构建 Electron 应用
2017/12/25 Javascript
微信小程序动态添加和删除组件的现实
2020/02/28 Javascript
jquery+ajax实现异步上传文件显示进度条
2020/08/17 jQuery
k8s node节点重新加入master集群的实现
2021/02/22 Javascript
[00:33]2016完美“圣”典风云人物:BurNIng宣传片
2016/12/10 DOTA
Python函数学习笔记
2008/10/07 Python
Python中使用PDB库调试程序
2015/04/05 Python
Python解析json文件相关知识学习
2016/03/01 Python
Python 分发包中添加额外文件的方法
2019/08/16 Python
html5 offlline 缓存使用示例
2013/06/24 HTML / CSS
前端实现弹幕效果的方法总结(包含css3和canvas的实现方式)
2018/07/12 HTML / CSS
美国标志性加大尺码时装品牌:Ashley Stewart
2016/12/15 全球购物
30年同学聚会邀请函
2014/01/25 职场文书
服务员岗位职责
2014/01/29 职场文书
小学防溺水制度
2014/01/29 职场文书
2014年国庆节广播稿
2014/09/19 职场文书
2016年情人节广告语
2016/01/28 职场文书
Opencv实现二维直方图的计算及绘制
2021/07/21 Python