您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關Mint-ui 框架中出現Datetime Picker和Popup組件滾動穿透問題怎么解決,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
在移動端開發中使用到了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" > <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 框架中出現Datetime Picker和Popup組件滾動穿透問題怎么解決就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。