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


Posted in Javascript onNovember 04, 2020

在移动端开发中使用到了Mint-ui组件库,其中有两个组件Popup组件和Datetime Picker存在滚动性穿透问题,官方文档最新版并没有解决这个问题。

现象还原

官方地址

手机扫码查看demo,查看两个组件Popup组件和Datetime的例子演示。

问题原因

HTML5触摸事件touchmove事件:当手指在屏幕上滑动的时候连续地触发

所以当激活出组件Popup组件和Datetime Picker的弹出层时,我们在弹层选择内容时上下连续滑动是会触发该事件

解决思路

在弹出层出现之后阻止body的touchmove事件,在弹层消失之后移除阻止事件

使用mint-ui组件库的解决方式

Popup组件

// 官方实例

<mt-popup
 v-model="popupVisible"
 position="bottom">
 ...
</mt-popup>

// 解决方式,通过监听popupVisible变量,在弹窗出现后禁止bode节点touchMove事件,弹窗消失后恢复body节点的touchMove事件

const handler = function(e) {
  e.preventDefault();
}

// vue实例内
watch: {
  popupVisible: function (val) {
   if(val) {
     document.getElementsByTagName('body')[0].addEventListener('touchmove', this.handler, { passive: false });
   } else {
     document.getElementsByTagName('body')[0].addEventListener('touchmove', this.handler, { passive: false });
   }
  }
}

Datetime Picker

// 官方实例

<mt-datetime-picker
  ref="picker"
  type="time"
  v-model="pickerValue">
</mt-datetime-picker>

// 解决方式:这个组件比较坑,由于Datetime Picker没有提供弹窗显示和隐藏的绑定变量,所以我们无采用解决popup的方式解决问题,只能通过打开事件,确认事件、取消事件,点击蒙层弹窗消失这几个时间点去解决。官方给出的属性方法只支持确认事件,打开事件。没有明文给出取消事件的回调函数,更不支持点击蒙层弹窗消失事件,所以很坑。

<mt-datetime-picker
  ref="picker"
  type="time"
  v-model="pickerValue"
  @confirm="confirm">
</mt-datetime-picker>

const handler = function(e) {
  e.preventDefault();
}

// vue实例内

methods: {
  openPicker() { // 打开事件
    document.getElementsByTagName('body')[0].addEventListener('touchmove', this.handler, { passive: false });
    this.$refs.picker.open();
  },
  confirm() { // 确认事件
    document.getElementsByTagName('body')[0].addEventListener('touchmove', this.handler, { passive: false });
  }
}

此时还缺一个取消回调还有蒙层点击事件,然后看源码其实mint-ui源码里是支持取消回调事件的,另外面,惊喜的是在2.0版本之后的mint-ui给Datetime picker组件 新加了visible-change事件和closeOnClickModal属性(传送门),但官方文档依旧没更新出来这些属性。现在就很好的解决了上述问题。

而且有了visible-change事件就可以不用按上述思路解决了。组件部分源码如下:

props: {
  ...,
  closeOnClickModal: {
    type: Boolean,
    default: true
  }
},
watch: {
  ...,
 visible(val) {
  this.$emit('visible-change', val);
 }
},
...
<span class="mint-datetime-action mint-datetime-cancel" @click="visible = false;$emit('cancel')">{{ cancelText }}</span>

直接一个visible-change方法搞定

<mt-datetime-picker
  ref="picker"
  type="time"
  v-model="pickerValue"
  @confirm="confirm"
  @visible-change=""handleValueChange>
</mt-datetime-picker>

const handler = function(e) {
  e.preventDefault();
}

// vue实例内
methods: {
  handleValueChange: function (val) {
   if(val) {
     document.getElementsByTagName('body')[0].addEventListener('touchmove', this.handler, { passive: false });
   } else {
     document.getElementsByTagName('body')[0].addEventListener('touchmove', this.handler, { passive: false });
   }
  }
}

上面的方法已经可以解决项目遇到到的坑了,但是每个页面用到组件Popup组件和Datetime Picker都需要去加上这段代码,当页面存在多个Popup组件就需要监听多个变量。

更好的解决方式(上述方式在网上也有类似解决方式,自己补充了visible-change方案)

在项目中按上面那样解决太麻烦了,所以又想了一个体验比较好的解决方案,因为弹层的出现和消失会触发组件更新,所以想到和可以全局注册一个指令v-roll,运用指令的componentUpdated钩子来实现这个功能。代码如下

// 全局注册指令
const handler = (e) => {
 e.preventDefault();
};
Vue.directive('roll', {
 componentUpdated(el, binding) {
  if (binding.value) {
   document.getElementsByTagName('body')[0].addEventListener('touchmove', handler, { passive: false });
  } else {
   document.getElementsByTagName('body')[0].removeEventListener('touchmove', handler, { passive: false });
  }
 }
});

这样一来精简了很多代码,其他开发者用到时只需要给对应组件加一个v-roll指令即可。

// popup组件处理方式
<mt-popup
 v-model="popupVisible"
 v-roll:visible=popupVisible>
 ...
</mt-popup>

// mt-datetime-picker组件处理方式
<mt-datetime-picker 
 ref="datePicker" 
 v-model="date"
 @visible-change="handleVisibleChange"
 v-roll:visible=pVisible
 ...>
</mt-datetime-picker>
...
data: {
  pVisible: false 
},
methods: {
  handleVisibleChange (isVisible) {
    this.pVisible = isVisible;
  }
}

接着还遇到一个坑,当同一个视图页面存在多个组件Popup组件和Datetime Picker使用指令时,会集体触发指令。所以会相互覆盖,如打开A弹层,阻止页面touchMove事件,但另外一个popuu组件也触发钩子函数,又取消阻止touchMove事件,导致没效果。

componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。// 这里目前自己的理解是由于弹层出现和消失导致所在视图界面节点更新,所以其他节点绑定了v-roll指令的钩子也被触发了

解决方案:对于一个视图内使用多个多个组件Popup组件和Datetime Picker的,给指令传入数组类型

// 全局注册指令
const handler = (e) => {
 e.preventDefault();
};
Vue.directive('roll', {
 componentUpdated(el, binding) {
  if (binding.value instanceof Array) {
   const visible = binding.value.some(e => e); // 当视图所有控制弹层的变量存在一个是true,即可阻止touchmove事件
   if (visible) {
    document.getElementsByTagName('body')[0].addEventListener('touchmove', handler, { passive: false });
   } else {
    document.getElementsByTagName('body')[0].removeEventListener('touchmove', handler, { passive: false });
   }
  } else if (typeof binding.value === 'boolean') {
   if (binding.value) {
    document.getElementsByTagName('body')[0].addEventListener('touchmove', handler, { passive: false });
   } else {
    document.getElementsByTagName('body')[0].removeEventListener('touchmove', handler, { passive: false });
   }
  }
 }
});

// popup组件处理方式
<mt-popup
 v-model="popupVisible"
 v-roll:visible=[popupVisible, pVisible]>
 ...
</mt-popup>

// mt-datetime-picker组件处理方式
<mt-datetime-picker 
 ref="datePicker" 
 v-model="date"
 @visible-change="handleVisibleChange"
 v-roll:visible=[popupVisible, pVisible]
 ...>
</mt-datetime-picker>
...
data: {
  pVisible: false 
},
methods: {
  handleVisibleChange (isVisible) {
    this.pVisible = isVisible;
  }
}

目前mint-ui还未修复该问题,所以暂且使用上述方案解决。一开始打算改源码,但是不现实,因为以后可能需要更新mint-ui版本。大家也可以帮忙看下以上解决方式是否有坑。

补充知识:Vue中使用mint-ui的日期插件时在ios上会有滚动穿透问题

问题:在ios上选择日期上下滑动时,整个页面会跟着滚动,安卓是正常的

解决方法就是在日期弹出层出现的时候禁止页面的默认滚动机制,日期弹出层消失的时候解除禁止页面的默认滚动机制

1.调用日期组件

<div class="datePicker" style="z-index: 9999">
   <mt-datetime-picker
    type="date"
    ref="picker"
    v-model="nowTime"
    year-format="{value} 年"
    month-format="{value} 月"
    date-format="{value} 日"
    @confirm="handleConfirm"
    :startDate="startDate"
    :endDate="endDate"
   >
   </mt-datetime-picker>
  </div>

2.设置监听函数

data () {
    return {
     birthday:"", //出生日期
     startDate: new Date('1952'),
     endDate:new Date(),
     nowTime:'1992-09-15',
    
     /*---------监听函数--------------*/
     handler:function(e){e.preventDefault();}
    }
   },
   methods:{
    /*解决iphone页面层级相互影响滑动的问题*/
    closeTouch:function(){
     document.getElementsByTagName("body")[0].addEventListener('touchmove',
      this.handler,{passive:false});//阻止默认事件
     console.log("closeTouch haved happened.");
    },
    openTouch:function(){
     document.getElementsByTagName("body")[0].removeEventListener('touchmove',
      this.handler,{passive:false});//打开默认事件
     console.log("openTouch haved happened.");
    },
 }

然后监听,弹窗出现消失的时候调用相应的方法

//侦听属性
watch:{
  signReasonVisible:function(newvs,oldvs){//picker关闭没有回调函数,所以侦听该属性替代
    if(newvs){
      this.closeTouch();
    }else{
      this.openTouch();
    }
  }
},

以下为datetime-picker的处理:(openPicker1为触发打开选择器的事件, handleConfirm (data)是选中日期后的回调函数)

openPicker () {
      this.$refs.picker.open();
      this.closeTouch();//关闭默认事件
 
     },
     handleConfirm (data) {
      let date = moment(data).format('YYYY-MM-DD')
      this.birthday = date;
      this.openTouch();//打开默认事件
 
     },

然后就解决了这个问题!

以上这篇解决Mint-ui 框架Popup和Datetime Picker组件滚动穿透的问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js异步加载的三种解决方案
Mar 04 Javascript
JS动态获取当前时间,并写到特定的区域
May 03 Javascript
jQuery之自动完成组件的深入解析
Jun 19 Javascript
JS的Document属性和方法小结
Sep 17 Javascript
jquery实现的伪分页效果代码
Oct 29 Javascript
基于JS实现导航条之调用网页助手小精灵的方法
Jun 17 Javascript
jQuery页面弹出框实现文件上传
Feb 09 Javascript
JS实现元素上下左右移动效果
Oct 18 Javascript
webpack打包并将文件加载到指定的位置方法
Feb 22 Javascript
解析Json字符串的三种方法日常常用
May 02 Javascript
vue 地区选择器v-distpicker的常用功能
Jul 23 Javascript
WebStorm无法正确识别Vue3组合式API的解决方案
Feb 18 Vue.js
基于js实现的图片拖拽排序源码实例
Nov 04 #Javascript
在vant中使用时间选择器和popup弹出层的操作
Nov 04 #Javascript
vue 判断两个时间插件结束时间必选大于开始时间的代码
Nov 04 #Javascript
vant 时间选择器--开始时间和结束时间实例
Nov 04 #Javascript
Vue绑定用户接口实现代码示例
Nov 04 #Javascript
vant picker+popup 自定义三级联动案例
Nov 04 #Javascript
解决vue-pdf查看pdf文件及打印乱码的问题
Nov 04 #Javascript
You might like
IIS6.0+PHP5.x+MySQL5.x+Zend3.0x+GD+phpMyAdmin2.8x通用安装实例(已经完成)
2006/12/06 PHP
百度工程师讲PHP函数的实现原理及性能分析(二)
2015/05/13 PHP
PHP简单获取多个checkbox值的方法
2016/06/13 PHP
Laravel中如何增加自定义全局函数详解
2017/05/09 PHP
Apache站点配置SSL强制跳转443
2021/03/09 Servers
jquery动态增加text元素以及删除文本内容实例代码
2013/07/01 Javascript
extjs 分页使用jsp传递数据示例
2014/07/29 Javascript
JavaScript访问字符串中单个字符的两种方法
2015/07/03 Javascript
jquery实现的动态回到顶部特效代码
2015/10/28 Javascript
微信小程序 实现tabs选项卡效果实例代码
2016/10/31 Javascript
脚本div实现拖放功能(两种)
2017/02/13 Javascript
使用MUI框架模拟手机端的下拉刷新和上拉加载功能
2017/09/04 Javascript
解决vue-cli单页面手机应用input点击手机端虚拟键盘弹出盖住input问题
2018/08/25 Javascript
JavaScript遍历DOM元素的常见方式示例
2019/02/16 Javascript
Vue2.0+Vux搭建一个完整的移动webApp项目的示例
2019/03/19 Javascript
Typescript 中的 interface 和 type 到底有什么区别详解
2019/06/18 Javascript
layui2.0使用table+laypage实现真分页
2019/07/27 Javascript
JavaScript图像放大镜效果实现方法详解
2020/06/28 Javascript
vue 通过绑定事件获取当前行的id操作
2020/07/27 Javascript
three.js 利用uv和ThreeBSP制作一个快递柜功能
2020/08/18 Javascript
[00:58]PWL开团时刻DAY5——十人开雾0换5
2020/11/04 DOTA
Python中文竖排显示的方法
2015/07/28 Python
Python加载带有注释的Json文件实例
2018/05/23 Python
浅谈Python采集网页时正则表达式匹配换行符的问题
2018/12/20 Python
Python变量类型知识点总结
2019/02/18 Python
django foreignkey(外键)的实现
2019/07/29 Python
详解如何将 Canvas 绘制过程转为视频
2021/01/25 HTML / CSS
美国便宜的横幅和标志印刷在线:Best of Signs
2019/05/29 全球购物
Java基础面试题
2012/11/02 面试题
数控专业大学生的自我鉴定
2013/11/13 职场文书
银行演讲稿范文
2014/01/03 职场文书
公司总经理助理岗位职责
2014/07/09 职场文书
领导干部四风问题自我剖析材料
2014/09/25 职场文书
2014年乡镇安全生产工作总结
2014/12/02 职场文书
Appium中scroll和drag_and_drop根据元素位置滑动
2022/02/15 Python
python实现学员管理系统(面向对象版)
2022/06/05 Python