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

一、问题描述

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

二、解决方案思考

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

  • 使用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 解决弹窗滚动穿透的资料请关注我们其它相关文章!

(0)

相关推荐

  • vue打开子组件弹窗都刷新功能的实现

    vue如何一打开子组件弹窗都刷新? 在父页面中给子组件同时绑定:visible.sync和v-if :visible.sync="paramAddDialog" v-if="paramAddDialog" 整体代码: <el-dialog title="绑定其他更多的账户" width="1200px" align="center" :visible.sync="paramAddDialog

  • Vue自定义全局弹窗组件操作

    写在前面 页面中会有很多时候需要弹窗提示,我们可以写一个弹窗组件,但是如果每个页面都引入这个组件,太麻烦了,所以我们将它变成全局组件,需要用的时候直接通过JS调用即可,不需要在每个页面引入了 效果图 弹窗组件 新建一个弹窗的组件--popup.vue <template> <transition name='fade'> <!-- 蒙版 --> <div class="mask" v-if="show" @touchmove

  • vue 实现一个简单的全局调用弹窗案例

    1.实现效果图 2.第一步,新建一个.vue文件 定义一个弹框的基本模板 style样式放在了文章的最底部,如果需要看效果,需要将样式放入这个vue文件里,样式是用less写的,需要你的项目引入less 注意:我这里的组件右上角关闭是一张图片 需要换成你自己本地的路径 <template> <div id="tip_alertModal"> <div class="t-alert-mask"></div> <di

  • vue实现抽屉弹窗效果

    本文实例为大家分享了vue实现抽屉弹窗效果的具体代码,供大家参考,具体内容如下 以下代码比较简单.主要就是实现 侧边弹窗而且不会影响页面操作的方式,求点赞!!!不多说直接贴代码. <template> <div> <div :class='{"itemCount":true,"leftT":!leftShow,"left":leftShow}'>//这种写法是动态获取样式 <div style="

  • 使用VUE实现在table中文字信息超过5个隐藏鼠标移到时弹窗显示全部

    具体代码如下所示: <template> <div> <table> <tr v-for="item in tableData" :value="item.value" :key="item"> <td> <div> <template> {{item.id}} </template> </div> </td> <td&g

  • vue实现点击按钮“查看详情”弹窗展示详情列表操作

    html: <template> <div> <Modal v-model="classStatus" width="900" title="详情:" :styles="{top: '80px'}"> <Table stripe class="task-table" :columns="columnsName4" :data="task

  • vue集成openlayers加载geojson并实现点击弹窗教程

    本文实例为大家分享了vue+openlayers加载geojson并实现点击弹窗教程,供大家参考,具体内容如下 第一步:安装vue-cli cnpm install -g @vue/cli 第二步:新建一个项目 1.新建项目 (vue-openlayers为项目名),并选择default模版 vue create vue-openlayers 2.安装openlayers cnpm i -S ol 第三步:写业务代码 1.删除掉HelloWorld.vue 新建 olmap.vue组件 comp

  • vue 解决mintui弹窗弹起来,底部页面滚动bug问题

    经过dom层层注释缩小反馈终于找到问题所在. 问题经过 我在弹起弹窗的时候,设置了popupVisible为true 然后触发了vue的updated生命周期钩子函数 然后我在这个函数里面做了去this.$refs.container.offsetHeight导致页面重绘 然后就导致了底部页面向上滚动 解决办法 去掉updated函数里面的重绘方法 补充知识:项目总结之关于vue中使用mint-ui的mt-popup出现滚动穿透问题的解决总结 说实话,使用Mint-ui这个ui组件的过程中遇到了

  • Vue中关闭弹窗组件时销毁并隐藏操作

    背景:在dialog弹窗组件中执行mounted钩子,将数据初始化,等取消关闭弹窗后,发现mounted钩子不执行 原因:在vue的生命周期中,在页面初始化的时候mounted只会执行一次,关闭弹窗页面并没有销毁,所以不会再次执行 <select-experience-group :trialMoneyRecordID=trialMoneyRecordID :showExperienceGroup='showExperienceGroup' @closeCover="handleExper

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

    一.问题描述 在移动端的H5页面中,我们经常会遇到 "点击按钮-->弹窗-->选择选项" 这样的场景.而在选项过多出现滚动条时,滚动滚动条至容器的底部或者顶部.再往上或往下拖动滚动条时,滚动动作会出现穿透,这时候底部的body也会一起滚动. 问题总结:内容在滚动到容器的顶部或者底部时,再向上或向下 强行滚动 ,则出现滚动穿透 二.解决方案思考 参考了网上一大堆的解决方法,大可分为三类方法.经过认真的思考和分析,个人的总结如下: 使用js去控制和改变css 1. 弹窗出现 1

  • vue 解决移动端弹出键盘导致页面fixed布局错乱的问题

    话不多说,直接上问题图片 这里确认按钮是fixed布局 bottom:0 弹出键盘之后按钮被顶到了键盘上面 网上搜到的解决方案有两种, 一种是监听页面高度(我采用的这种) 一种是监听软键盘事件(ios和安卓实现方式不同,未采用) 下面是实现代码 data() { return { docmHeight: document.documentElement.clientHeight ||document.body.clientHeight, showHeight: document.document

  • vue 弹框产生的滚动穿透问题的解决

    最近开发过程中遇到一些小问题(似乎问题总是那么多),但一直没什么时间去优化与解决.程序员不能被业务绑架,有时间还是花点在代码,开始这次的vue尝试吧. 首先定义一个全局样式: .noscroll{ position: fixed; left: 0; top: 0; width: 100%; } 创建一个dom.js文件,定义几个方法: export function hasClass(el, className) { let reg = new RegExp('(^|\\s)' + classN

  • vue实现移动端弹出键盘功能(防止页面fixed布局错乱)

    监听页面高度,当键盘弹出时,将按钮隐藏. data() { return { docmHeight: document.documentElement.clientHeight ||document.body.clientHeight, showHeight: document.documentElement.clientHeight ||document.body.clientHeight, hideshow:true //显示或者隐藏footer } }, watch: { //监听显示高度

  • vue中pc移动滚动穿透问题及解决

    目录 vue pc移动滚动穿透问题 上层无滚动(很简单直接@touchmove.prevent) 上层有滚动 滑动穿透终极解决方案 问题描述 问题探究 原理探究 vue pc移动滚动穿透问题 上层无滚动(很简单直接@touchmove.prevent) <div @touchmove.prevent> 我是里面的内容 </div> 上层有滚动 如果上层需要滚动的话,那么固定的时候先获取 body 的滑动距离,然后用 fixed 固定,用 top 模拟滚动距离:不固定的时候用获取 t

  • vue中利用iscroll.js解决pc端滚动问题

    项目中经常遇到区域超出部分会出现滚动条,滚动条在pc端可以通过鼠标滚轮控制上下,在移动端可以通过鼠标拖动页面进行滚动,这两种场景都是符合用户习惯,然而这种滚动条一般都是竖[vertical]项滚动条,如果pc端出现横向滚动条[horizontal],在不做处理的情况下,你只能用鼠标拖动横向滚动条按钮[scrollerbar]展示滚动区域,而且为了美观,一般滚动条会进行样式编写或者隐藏,那么横向区域默认情况下就没法滚动. 二.描述 现为了解决pc端滚动区域能像移动端一样,能够通过鼠标拖动滚动区域直

  • Vue项目移动端滚动穿透问题的实现

    概述 今天在做 Vue 移动端项目的时候遇到了滚动穿透问题,在网上查资料后,选取了我觉得最好的方法,记录下来供以后开发时参考,相信对其他人也有用. 上层无需滚动 如果上层无需滚动的话,直接屏蔽上层的 touchmove 事件即可.示例如下: <div @touchmove.prevent> 我是里面的内容 </div> 上层需要滚动 如果上层需要滚动的话,那么固定的时候先获取 body 的滑动距离,然后用 fixed 固定,用 top 模拟滚动距离:不固定的时候用获取 top 的值

  • 解决Mint-ui 框架Popup和Datetime Picker组件滚动穿透的问题

    在移动端开发中使用到了Mint-ui组件库,其中有两个组件Popup组件和Datetime Picker存在滚动性穿透问题,官方文档最新版并没有解决这个问题. 现象还原 官方地址 手机扫码查看demo,查看两个组件Popup组件和Datetime的例子演示. 问题原因 HTML5触摸事件touchmove事件:当手指在屏幕上滑动的时候连续地触发 所以当激活出组件Popup组件和Datetime Picker的弹出层时,我们在弹层选择内容时上下连续滑动是会触发该事件 解决思路 在弹出层出现之后阻止

  • 解决微信小程序中的滚动穿透问题

    Mask-Scroll > 原码地址 * 蒙层防穿透问题 > 蒙层穿透就是,当你用fixed 布局让蒙层显示的时候, 手指滑动屏幕会出现底部内容也滑动的现象. 如图: 当蒙层出现的时候,你滚动屏幕,底部内容也一起跟着滚动. 这就是蒙层穿透, 也可以叫 '滚动穿透'. 当然出现这种情况, 用户体验当然是不好的了. 所以作为一个有点追求的工程师当然是不允许这种情况的发生了(手动狗头...) ## 解决方案 这种要分情况, 当蒙层没有滚动条的时候. 当蒙层出现滚动条的时候 1. 当弹窗没有滚动条的时

随机推荐