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项目设置resolves.alias: '@'路径并适配webstorm
Dec 02 Vue.js
vue使用echarts图表自适应的几种解决方案
Dec 04 Vue.js
Vue3 实现双盒子定位Overlay的示例
Dec 22 Vue.js
vue-cli4.0多环境配置变量与模式详解
Dec 30 Vue.js
基于Vue2实现移动端图片上传、压缩、拖拽排序、拖拽删除功能
Jan 05 Vue.js
Vue仿Bibibili首页的问题
Jan 21 Vue.js
vue如何使用rem适配
Feb 06 Vue.js
一篇文章告诉你如何实现Vue前端分页和后端分页
Feb 18 Vue.js
关于Vue中的options选项
Mar 22 Vue.js
深入讲解Vue中父子组件通信与事件触发
Mar 22 Vue.js
vue.js 使用原生js实现轮播图
Apr 26 Vue.js
vue el-table实现递归嵌套的示例代码
Aug 14 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
zend Framework中的Layout(模块化得布局)详解
2013/06/28 PHP
根据ip调用新浪api获取城市名并转成拼音
2014/03/07 PHP
PHP文件生成的图片无法使用CDN缓存的解决方法
2015/06/20 PHP
php检查函数必传参数是否存在的实例详解
2017/08/28 PHP
JavaScript 用Node.js写Shell脚本[译]
2012/09/20 Javascript
jquery修改属性值实例代码(设置属性值)
2014/01/06 Javascript
jQuery中parents()方法用法实例
2015/01/07 Javascript
JS操作HTML自定义属性的方法
2015/02/10 Javascript
PHP 数组current和next用法分享
2015/03/05 Javascript
Vue.js系列之项目结构说明(2)
2017/01/03 Javascript
canvas实现简易的圆环进度条效果
2017/02/28 Javascript
jQuery插件FusionCharts实现的Marimekko图效果示例【附demo源码】
2017/03/24 jQuery
JS给按钮添加跳转功能类似a标签
2017/05/30 Javascript
了解VUE的render函数的使用
2017/06/08 Javascript
iframe高度自适应及隐藏滚动条的实例详解
2017/09/29 Javascript
vue 实现搜索的结果页面支持全选与取消全选功能
2019/05/10 Javascript
jquery UI实现autocomplete在获取焦点时得到显示列表功能示例
2019/06/04 jQuery
深入学习TypeScript 、React、 Redux和Ant-Design的最佳实践
2019/06/17 Javascript
js实现指定时间倒计时效果
2019/08/26 Javascript
vue-cli3使用mock数据的方法分析
2020/03/16 Javascript
通过实例解析js可枚举属性与不可枚举属性
2020/12/02 Javascript
[06:44]2014DOTA2国际邀请赛-钥匙体育馆开战 开幕式振奋人心
2014/07/19 DOTA
python通过索引遍历列表的方法
2015/05/04 Python
python字符串连接方法分析
2016/04/12 Python
Python中json格式数据的编码与解码方法详解
2016/07/01 Python
浅谈Python由__dict__和dir()引发的一些思考
2017/10/30 Python
使用DataFrame删除行和列的实例讲解
2018/04/08 Python
Python第三方Window模块文件的几种安装方法
2018/11/22 Python
对Keras中predict()方法和predict_classes()方法的区别说明
2020/06/09 Python
Lookfantastic西班牙官网:英国知名美妆购物网站
2018/06/13 全球购物
矫正人员思想汇报
2014/01/08 职场文书
2014年五一促销活动方案
2014/03/09 职场文书
银行内勤岗位职责
2014/04/09 职场文书
《恐龙》教学反思
2014/04/27 职场文书
音乐之声音乐广播稿
2014/09/10 职场文书
2014年基层党建工作总结
2014/11/11 职场文书