解決Mint-ui 框架Popup和Datetime Picker組件滾動(dòng)穿透的問題
在移動(dòng)端開發(fā)中使用到了Mint-ui組件庫(kù),其中有兩個(gè)組件Popup組件和Datetime Picker存在滾動(dòng)性穿透問題,官方文檔最新版并沒有解決這個(gè)問題。
現(xiàn)象還原
官方地址
手機(jī)掃碼查看demo,查看兩個(gè)組件Popup組件和Datetime的例子演示。
問題原因
HTML5觸摸事件touchmove事件:當(dāng)手指在屏幕上滑動(dòng)的時(shí)候連續(xù)地觸發(fā)
所以當(dāng)激活出組件Popup組件和Datetime Picker的彈出層時(shí),我們?cè)趶棇舆x擇內(nèi)容時(shí)上下連續(xù)滑動(dòng)是會(huì)觸發(fā)該事件
解決思路
在彈出層出現(xiàn)之后阻止body的touchmove事件,在彈層消失之后移除阻止事件
使用mint-ui組件庫(kù)的解決方式
Popup組件
// 官方實(shí)例
<mt-popup v-model="popupVisible" position="bottom"> ... </mt-popup>
// 解決方式,通過監(jiān)聽popupVisible變量,在彈窗出現(xiàn)后禁止bode節(jié)點(diǎn)touchMove事件,彈窗消失后恢復(fù)body節(jié)點(diǎn)的touchMove事件
const handler = function(e) { e.preventDefault(); } // vue實(shí)例內(nèi) 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
// 官方實(shí)例
<mt-datetime-picker ref="picker" type="time" v-model="pickerValue"> </mt-datetime-picker>
// 解決方式:這個(gè)組件比較坑,由于Datetime Picker沒有提供彈窗顯示和隱藏的綁定變量,所以我們無采用解決popup的方式解決問題,只能通過打開事件,確認(rèn)事件、取消事件,點(diǎn)擊蒙層彈窗消失這幾個(gè)時(shí)間點(diǎn)去解決。官方給出的屬性方法只支持確認(rèn)事件,打開事件。沒有明文給出取消事件的回調(diào)函數(shù),更不支持點(diǎn)擊蒙層彈窗消失事件,所以很坑。
<mt-datetime-picker ref="picker" type="time" v-model="pickerValue" @confirm="confirm"> </mt-datetime-picker> const handler = function(e) { e.preventDefault(); }
// vue實(shí)例內(nèi)
methods: { openPicker() { // 打開事件 document.getElementsByTagName('body')[0].addEventListener('touchmove', this.handler, { passive: false }); this.$refs.picker.open(); }, confirm() { // 確認(rèn)事件 document.getElementsByTagName('body')[0].addEventListener('touchmove', this.handler, { passive: false }); } }
此時(shí)還缺一個(gè)取消回調(diào)還有蒙層點(diǎn)擊事件,然后看源碼其實(shí)mint-ui源碼里是支持取消回調(diào)事件的,另外面,驚喜的是在2.0版本之后的mint-ui給Datetime picker組件 新加了visible-change事件和closeOnClickModal屬性(傳送門),但官方文檔依舊沒更新出來這些屬性?,F(xiàn)在就很好的解決了上述問題。
而且有了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>
直接一個(gè)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實(shí)例內(nèi) 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 }); } } }
上面的方法已經(jīng)可以解決項(xiàng)目遇到到的坑了,但是每個(gè)頁(yè)面用到組件Popup組件和Datetime Picker都需要去加上這段代碼,當(dāng)頁(yè)面存在多個(gè)Popup組件就需要監(jiān)聽多個(gè)變量。
更好的解決方式(上述方式在網(wǎng)上也有類似解決方式,自己補(bǔ)充了visible-change方案)
在項(xiàng)目中按上面那樣解決太麻煩了,所以又想了一個(gè)體驗(yàn)比較好的解決方案,因?yàn)閺棇拥某霈F(xiàn)和消失會(huì)觸發(fā)組件更新,所以想到和可以全局注冊(cè)一個(gè)指令v-roll,運(yùn)用指令的componentUpdated鉤子來實(shí)現(xiàn)這個(gè)功能。代碼如下
// 全局注冊(cè)指令 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 }); } } });
這樣一來精簡(jiǎn)了很多代碼,其他開發(fā)者用到時(shí)只需要給對(duì)應(yīng)組件加一個(gè)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; } }
接著還遇到一個(gè)坑,當(dāng)同一個(gè)視圖頁(yè)面存在多個(gè)組件Popup組件和Datetime Picker使用指令時(shí),會(huì)集體觸發(fā)指令。所以會(huì)相互覆蓋,如打開A彈層,阻止頁(yè)面touchMove事件,但另外一個(gè)popuu組件也觸發(fā)鉤子函數(shù),又取消阻止touchMove事件,導(dǎo)致沒效果。
componentUpdated:指令所在組件的 VNode 及其子 VNode 全部更新后調(diào)用。// 這里目前自己的理解是由于彈層出現(xiàn)和消失導(dǎo)致所在視圖界面節(jié)點(diǎn)更新,所以其他節(jié)點(diǎn)綁定了v-roll指令的鉤子也被觸發(fā)了
解決方案:對(duì)于一個(gè)視圖內(nèi)使用多個(gè)多個(gè)組件Popup組件和Datetime Picker的,給指令傳入數(shù)組類型
// 全局注冊(cè)指令 const handler = (e) => { e.preventDefault(); }; Vue.directive('roll', { componentUpdated(el, binding) { if (binding.value instanceof Array) { const visible = binding.value.some(e => e); // 當(dāng)視圖所有控制彈層的變量存在一個(gè)是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還未修復(fù)該問題,所以暫且使用上述方案解決。一開始打算改源碼,但是不現(xiàn)實(shí),因?yàn)橐院罂赡苄枰耺int-ui版本。大家也可以幫忙看下以上解決方式是否有坑。
補(bǔ)充知識(shí):Vue中使用mint-ui的日期插件時(shí)在ios上會(huì)有滾動(dòng)穿透問題
問題:在ios上選擇日期上下滑動(dòng)時(shí),整個(gè)頁(yè)面會(huì)跟著滾動(dòng),安卓是正常的
解決方法就是在日期彈出層出現(xiàn)的時(shí)候禁止頁(yè)面的默認(rèn)滾動(dòng)機(jī)制,日期彈出層消失的時(shí)候解除禁止頁(yè)面的默認(rèn)滾動(dòng)機(jī)制
1.調(diào)用日期組件
<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.設(shè)置監(jiān)聽函數(shù)
data () { return { birthday:"", //出生日期 startDate: new Date('1952'), endDate:new Date(), nowTime:'1992-09-15', /*---------監(jiān)聽函數(shù)--------------*/ handler:function(e){e.preventDefault();} } }, methods:{ /*解決iphone頁(yè)面層級(jí)相互影響滑動(dòng)的問題*/ closeTouch:function(){ document.getElementsByTagName("body")[0].addEventListener('touchmove', this.handler,{passive:false});//阻止默認(rèn)事件 console.log("closeTouch haved happened."); }, openTouch:function(){ document.getElementsByTagName("body")[0].removeEventListener('touchmove', this.handler,{passive:false});//打開默認(rèn)事件 console.log("openTouch haved happened."); }, }
然后監(jiān)聽,彈窗出現(xiàn)消失的時(shí)候調(diào)用相應(yīng)的方法
//偵聽屬性 watch:{ signReasonVisible:function(newvs,oldvs){//picker關(guān)閉沒有回調(diào)函數(shù),所以偵聽該屬性替代 if(newvs){ this.closeTouch(); }else{ this.openTouch(); } } },
以下為datetime-picker的處理:(openPicker1為觸發(fā)打開選擇器的事件, handleConfirm (data)是選中日期后的回調(diào)函數(shù))
openPicker () { this.$refs.picker.open(); this.closeTouch();//關(guān)閉默認(rèn)事件 }, handleConfirm (data) { let date = moment(data).format('YYYY-MM-DD') this.birthday = date; this.openTouch();//打開默認(rèn)事件 },
然后就解決了這個(gè)問題!
以上這篇解決Mint-ui 框架Popup和Datetime Picker組件滾動(dòng)穿透的問題就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
vue.js 實(shí)現(xiàn)v-model與{{}}指令方法
這篇文章主要介紹了vue.js 實(shí)現(xiàn)v-model與{{}}指令方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-10-10Vue $emit $refs子父組件間方法的調(diào)用實(shí)例
今天小編就為大家分享一篇Vue $emit $refs子父組件間方法的調(diào)用實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-09-09elementUI表格多選框this.$refs.xxx.toggleRowSelection無效問題
這篇文章主要介紹了elementUI表格多選框this.$refs.xxx.toggleRowSelection無效問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11Vue通過Blob對(duì)象實(shí)現(xiàn)導(dǎo)出Excel功能示例代碼
這篇文章主要介紹了Vue通過Blob對(duì)象實(shí)現(xiàn)導(dǎo)出Excel功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07詳解vue computed的緩存實(shí)現(xiàn)原理
這篇文章主要介紹了vue computed的緩存實(shí)現(xiàn)原理,幫助大家更好的理解和學(xué)習(xí)使用vue,感興趣的朋友可以了解下2021-04-04vue post application/x-www-form-urlencoded如何實(shí)現(xiàn)傳參
這篇文章主要介紹了vue post application/x-www-form-urlencoded如何實(shí)現(xiàn)傳參問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04Vue彈窗的兩種實(shí)現(xiàn)方式實(shí)例詳解
這篇文章主要介紹了Vue彈窗的兩種實(shí)現(xiàn)方式,一種使用.sync修飾符另一種使用v-model,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-08-08關(guān)于eslint+prettier+husky的配置及說明
這篇文章主要介紹了關(guān)于eslint+prettier+husky的配置及說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07