解决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 相关文章推荐
JavaScript-世界上误解最深的语言分析
Aug 12 Javascript
Javascript 面向对象 命名空间
May 13 Javascript
location.href 在IE6中不跳转的解决方法与推荐使用代码
Jul 08 Javascript
jQuery实现简单二级下拉菜单
Apr 12 Javascript
JavaScript判断undefined类型的正确方法
Jun 30 Javascript
jQuery事件用法详解
Oct 06 Javascript
Ajax的概述与实现过程
Nov 18 Javascript
在node中如何使用 ES6
Apr 22 Javascript
解决Linux无法正常安装与卸载Node.js的方法
Jan 19 Javascript
vue项目如何刷新当前页面的方法
May 18 Javascript
微信小程序如何修改本地缓存key中单个数据的详解
Apr 26 Javascript
node.js中path路径模块的使用方法实例分析
Feb 13 Javascript
基于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
PHP 和 XML: 使用expat函数(二)
2006/10/09 PHP
精通php的十大要点(上)
2009/02/04 PHP
编译php 5.2.14+fpm+memcached(具体操作详解)
2013/06/18 PHP
推荐25款php中非常有用的类库
2014/09/29 PHP
PHP+MySQL存储数据常见中文乱码问题小结
2016/06/13 PHP
如何用PHP做到页面注册审核
2017/03/02 PHP
javascript 学习笔记(一)DOM基本操作
2011/04/08 Javascript
JavaScript内核之基本概念
2011/10/21 Javascript
在标题栏显示新消息提示,很多公司项目中用到这个方法
2011/11/04 Javascript
js实现图片从左往右渐变切换效果的方法
2015/02/06 Javascript
js实现大转盘抽奖游戏实例
2015/06/24 Javascript
JS动态添加iframe的代码
2015/09/14 Javascript
Javascript实现跑马灯效果的简单实例
2016/05/31 Javascript
BootStrap 智能表单实战系列(五) 表单依赖插件处理
2016/06/13 Javascript
Javascript的比较汇总
2016/07/25 Javascript
关于Ajax的原理以及代码封装详解
2017/09/08 Javascript
浅谈手写node可读流之流动模式
2018/06/01 Javascript
python字符串中的单双引
2017/02/16 Python
使用Django Form解决表单数据无法动态刷新的两种方法
2017/07/14 Python
python生成二维码的实例详解
2017/10/29 Python
使用sklearn进行对数据标准化、归一化以及将数据还原的方法
2018/07/11 Python
python多继承(钻石继承)问题和解决方法简单示例
2019/10/21 Python
tensorflow-gpu安装的常见问题及解决方案
2020/01/20 Python
解决pyinstaller打包运行程序时出现缺少plotly库问题
2020/06/02 Python
利用pipenv和pyenv管理多个相互独立的Python虚拟开发环境
2020/11/01 Python
pytorch 计算Parameter和FLOP的操作
2021/03/04 Python
CSS3田字格列表的样式编写方法
2018/11/22 HTML / CSS
财务会计人员求职的自我评价
2014/01/13 职场文书
教师产假请假条
2014/04/10 职场文书
小学生期末评语大全
2014/04/21 职场文书
动画设计系毕业生求职信
2014/07/15 职场文书
暑期社会实践心得体会
2014/09/02 职场文书
企业介绍信范文
2015/01/30 职场文书
大学生村官工作总结2015
2015/04/09 职场文书
Python读取文件夹下的所有文件实例代码
2021/04/02 Python
python not运算符的实例用法
2021/06/30 Python