解决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 css后面所带参数含义介绍
Aug 18 Javascript
ie 7/8不支持trim的属性的解决方案
May 23 Javascript
基于JavaScript实现网页倒计时自动跳转代码
Dec 28 Javascript
JS未跨域操作iframe里的DOM
Jun 01 Javascript
微信小程序开发之大转盘 仿天猫超市抽奖实例
Dec 08 Javascript
全面解析Node.js 8 重要功能和修复
Jun 02 Javascript
Angular 4依赖注入学习教程之Injectable装饰器(六)
Jun 04 Javascript
微信小程序实现城市列表选择
Jun 05 Javascript
了解JavaScript函数中的默认参数
May 30 Javascript
JQuery发送ajax请求时中文乱码问题解决
Nov 14 jQuery
CKEditor扩展插件:自动排版功能autoformat插件实现方法详解
Feb 06 Javascript
uni-app 自定义底部导航栏的实现
Dec 11 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 Smarty生成EXCEL文档的代码
2008/08/23 PHP
php自定义函数call_user_func和call_user_func_array详解
2011/07/14 PHP
php的array数组和使用实例简明教程(容易理解)
2014/03/20 PHP
php实现的Captcha验证码类实例
2014/09/22 PHP
php格式化时间戳显示友好的时间实现思路及代码
2014/10/23 PHP
用htc组件制作windows选项卡
2007/01/13 Javascript
javascript 获取图片颜色
2009/04/05 Javascript
Google Map V3 绑定气泡窗口(infowindow)Dom事件实现代码
2013/04/26 Javascript
jQuery插件实现文字无缝向上滚动效果代码
2016/02/25 Javascript
AngularJS中的$watch(),$digest()和$apply()区分
2016/04/04 Javascript
jQuery Mobile 和 Kendo UI 的比较
2016/05/05 Javascript
Bootstrap3学习笔记(三)之表格
2016/05/20 Javascript
Bootstrap框架实现广告轮播效果
2016/11/28 Javascript
微信小程序开发教程-手势解锁实例
2017/01/06 Javascript
jQuery实现文字超过1行、2行或规定的行数时自动加省略号的方法
2018/03/28 jQuery
详解JS实现简单的时分秒倒计时代码
2019/04/25 Javascript
js实现炫酷光感效果
2020/09/05 Javascript
利用python批量检查网站的可用性
2016/09/09 Python
python批量下载抖音视频
2019/06/17 Python
Win10下python 2.7与python 3.7双环境安装教程图解
2019/10/12 Python
pyMySQL SQL语句传参问题,单个参数或多个参数说明
2020/06/06 Python
基于python实现ROC曲线绘制广场解析
2020/06/28 Python
HTML5中form如何关闭自动完成功能的方法
2018/07/02 HTML / CSS
美国高级音响品牌:Master&Dynamic
2018/07/05 全球购物
在C语言中实现抽象数据类型什么方法最好
2014/06/26 面试题
params有什么用
2016/03/01 面试题
自学考试自我鉴定范文
2013/09/26 职场文书
宾馆总经理岗位职责
2014/02/14 职场文书
航海技术专业毕业生求职信
2014/04/06 职场文书
教师产假请假条范文
2014/04/10 职场文书
书香校园建设方案
2014/05/02 职场文书
优秀班集体先进事迹材料
2014/05/28 职场文书
新闻学专业求职信
2014/07/28 职场文书
医生学习党的群众路线教育实践活动心得体会
2014/11/03 职场文书
2015年预防青少年违法犯罪工作总结
2015/05/22 职场文书
html2 canvas svg不能识别的解决方案
2021/06/03 HTML / CSS