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中使用inheritAttrs实现组件的扩展性介绍
Dec 07 Vue.js
vue 基于abstract 路由模式 实现页面内嵌的示例代码
Dec 14 Vue.js
Vue——前端生成二维码的示例
Dec 19 Vue.js
vue实现按钮切换图片
Jan 20 Vue.js
vue form表单post请求结合Servlet实现文件上传功能
Jan 22 Vue.js
聊聊vue 中的v-on参数问题
Jan 29 Vue.js
vue实现倒计时功能
Mar 24 Vue.js
如何使用vue3打造一个物料库
May 08 Vue.js
解决vue $http的get和post请求跨域问题
Jun 07 Vue.js
Vue过滤器(filter)实现及应用场景详解
Jun 15 Vue.js
vue中 this.$set的使用详解
Nov 17 Vue.js
Vue+TypeScript中处理computed方式
Apr 02 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设计模式中的工厂模式
2008/06/12 PHP
sourcesafe管理phpproj文件的补充说明(downmoon)
2009/04/11 PHP
php 求质素(素数) 的实现代码
2011/04/12 PHP
php字符编码转换之gb2312转为utf8
2013/10/28 PHP
PHP如何将log信息写入服务器中的log文件
2015/07/29 PHP
PHP有序表查找之插值查找算法示例
2018/02/10 PHP
在JavaScript中实现命名空间
2006/11/23 Javascript
基于jquery的跟随屏幕滚动代码
2012/07/24 Javascript
jquery实现弹出窗口效果的实例代码
2013/11/28 Javascript
JS 获取HTML标签内的子节点的方法
2016/09/21 Javascript
webpack+vue.js快速入门教程
2016/10/12 Javascript
浅谈react 同构之样式直出
2017/11/07 Javascript
vue2.0 下拉框默认标题设置方法
2018/08/22 Javascript
使用Angular 6创建各种动画效果的方法
2018/10/10 Javascript
angular8和ngrx8结合使用的步骤介绍
2019/12/01 Javascript
Javascript异步编程async实现过程详解
2020/04/02 Javascript
tensorflow实现softma识别MNIST
2018/03/12 Python
windows10下python3.5 pip3安装图文教程
2018/04/02 Python
Python实现一个服务器监听多个客户端请求
2018/04/12 Python
pycharm设置注释颜色的方法
2018/05/23 Python
Python3实现腾讯云OCR识别
2018/11/27 Python
python文件写入write()的操作
2019/05/14 Python
Python Pandas对缺失值的处理方法
2019/09/27 Python
关于pycharm 切换 python3.9 报错 ‘HTMLParser‘ object has no attribute ‘unescape‘ 的问题
2020/11/24 Python
python飞机大战游戏实例讲解
2020/12/04 Python
英国在线发型和美容产品商店:Beauty Cutie
2019/04/27 全球购物
在C中是否有模拟继承等面向对象程序设计特性的好方法
2012/05/22 面试题
iostream与iostream.h的区别
2015/01/16 面试题
高分子材料个人求职信范文
2013/09/25 职场文书
幼儿园实习自我鉴定
2013/12/15 职场文书
项目开发计划书
2014/01/09 职场文书
打架检讨书300字
2014/02/02 职场文书
村党支部对照检查材料思想汇报
2014/09/28 职场文书
学生逃课检讨书1000字
2014/10/20 职场文书
2015年社区教育工作总结
2015/05/13 职场文书
Python爬虫之用Xpath获取关键标签实现自动评论盖楼抽奖(二)
2021/06/07 Python