vue項(xiàng)目中解決 IOS + H5 滑動(dòng)邊界橡皮筋彈性效果(解決思路)
問(wèn)題:
最近遇到一個(gè)問(wèn)題,我們?cè)谄髽I(yè)微信中的 H5 項(xiàng)目中需要用到table表格(支持懶加載 上劃加載數(shù)據(jù))。但是他們?cè)阪i頭、鎖列的情況下,依舊會(huì)出現(xiàn)邊界橡皮筋效果。就會(huì)顯示的很奇怪。
什么是ios橡皮筋效果:
我們知道元素超出所給定的高度會(huì)出現(xiàn)滾動(dòng)條 | 橫向的或縱向的,在ios手機(jī)上當(dāng)在全局范圍或局部范圍出現(xiàn)滾動(dòng)的地方時(shí),滑動(dòng)使?jié)L動(dòng)到頭時(shí)還可以繼續(xù)拖拽出一段距離的空白,松開(kāi)手時(shí)立刻回彈回去。雖不影響功能,但是操作有點(diǎn)別扭,感覺(jué)這個(gè)table表格滑動(dòng)的時(shí)候像是飄著上面的,不是固定的。
嘗試思路:
針對(duì)這個(gè)問(wèn)題我也嘗試了不同的方案都不盡人意;
1,首先想到先使用css解決,前后使用了絕對(duì)定位和固定定位來(lái)提高層級(jí),發(fā)現(xiàn)問(wèn)題依然存在;
2,然后是用css屬性&::-webkit-scrollbar { display: none;} 來(lái)隱藏滾動(dòng)條 ,還是不行,
3,后面引入網(wǎng)上的inobounce插件還是不行。此插件會(huì)禁用頁(yè)面的touchmove事件,導(dǎo)致頁(yè)面無(wú)法進(jìn)行滑動(dòng);
4,因?yàn)槲矣玫氖窃膖able來(lái)布局的,難道是table標(biāo)簽的問(wèn)題,后面我又重新用div來(lái)實(shí)現(xiàn)table表格的布局,發(fā)現(xiàn)還是會(huì)出現(xiàn)橡皮筋效果,說(shuō)明不是標(biāo)簽的問(wèn)題,是ios瀏覽器隱藏的特性;
解決方案:
最后我想到,既然我關(guān)不掉,那我自己完全控制滑動(dòng)好了;原理是禁用掉頁(yè)面touchumove的默認(rèn)的滑動(dòng)效果,使用元素的scrollLeft 和scrollTop這兩個(gè)原生的可寫(xiě)屬性,來(lái)進(jìn)行元素的上下和左右移動(dòng);
這樣做的好處在于,我自己完全控制了拖拽滑動(dòng)行為。壞處在于丟失了原生滑動(dòng)的慣性滑動(dòng),看著沒(méi)那么絲滑了。不過(guò)這點(diǎn)也可以后續(xù)通過(guò) JavaScript 來(lái)優(yōu)化(暫時(shí)還沒(méi)寫(xiě) ,寫(xiě)出來(lái)的效果不太好,不絲滑)。
效果圖如下:
可以上下左右滑動(dòng),是固頂?shù)?,向上滑?dòng)時(shí)標(biāo)題部分不會(huì)跟隨移動(dòng);
可以懶加載:當(dāng)下滑到觸底時(shí)會(huì)向父元素傳遞一個(gè)事件告訴父組件該請(qǐng)求下一頁(yè)的數(shù)據(jù)了;
主要實(shí)現(xiàn)代碼如下:
表格組件部分代碼如下:tableStrick.vue;
data() { return { listEle: null, // dom元素 // 記錄坐標(biāo) touchX: "", touchY: "", // 滑動(dòng)坐標(biāo) startX: 0, startY: 0, // 滑動(dòng)方向 direction: "", }; } mounted() { /* 獲取dom元素 這里最好不要用原生的dom獲取方式 */ this.listEle = this.$refs.main; /* 自己實(shí)現(xiàn)滾動(dòng)效果 不會(huì)出現(xiàn)滾動(dòng)回彈問(wèn)題 但是滾動(dòng)不絲滑了且沒(méi)有慣性 */ this.listEle.addEventListener("touchstart", this.touchstart, false); this.listEle.addEventListener("touchmove", this.touchmove, false); this.listEle.addEventListener("touchend", this.touchend, false); }, methods:{ /* 修改 瀏覽器默認(rèn)的滑動(dòng)容器行為 */ // 1,手指接觸屏幕 touchstart(event) { this.touchX = event.changedTouches[0].clientX; this.touchY = event.changedTouches[0].clientY; // 獲取此刻手指的橫坐標(biāo)startX和縱坐標(biāo)startY this.startX = event.touches[0].pageX; this.startY = event.touches[0].pageY; }, // 2, 手指滑動(dòng)的過(guò)程 touchmove(event) { event.preventDefault(); // 計(jì)算手指偏移量 const offsetX = event.changedTouches[0].clientX - this.touchX; const offsetY = event.changedTouches[0].clientY - this.touchY; // 觸摸的坐標(biāo) this.touchX = event.changedTouches[0].clientX; this.touchY = event.changedTouches[0].clientY; // 手指滑動(dòng)的方向 let moveEndX = event.changedTouches[0].pageX; let moveEndY = event.changedTouches[0].pageY; let X = moveEndX - this.startX; let Y = moveEndY - this.startY; // 注意 上下移動(dòng)只能是上劃或下劃 左右移動(dòng)也是同理 if (Math.abs(X) > Math.abs(Y) && X > 0) { // 開(kāi)始移動(dòng) console.log('我向右滑了) this.listEle.scrollLeft = this.listEle.scrollLeft - offsetX; this.direction = "right"; } else if (Math.abs(X) > Math.abs(Y) && X < 0) { console.log('我向左滑了) this.listEle.scrollLeft = this.listEle.scrollLeft - offsetX; this.direction = "left"; } else if (Math.abs(Y) > Math.abs(X) && Y > 0) { console.log('我向下滑了) this.listEle.scrollTop = this.listEle.scrollTop - offsetY; /* 設(shè)置滾動(dòng)到底的處理 */ this.scrollBottom(event); this.direction = "bottom"; } else if (Math.abs(Y) > Math.abs(X) && Y < 0) { console.log('我向上滑了) this.listEle.scrollTop = this.listEle.scrollTop - offsetY; this.direction = "top"; /* 設(shè)置滾動(dòng)到底的處理 */ this.scrollBottom(event); } else { this.direction = ""; } }, // 3,手指離開(kāi)屏幕 touchend(event) { this.touchX = event.changedTouches[0].clientX; this.touchY = event.changedTouches[0].clientY; // TODO 此處可以進(jìn)行優(yōu)化滾動(dòng)的慣性 暫未實(shí)現(xiàn) }, // 監(jiān)聽(tīng)滾動(dòng)條 注意 scroll 可能是橫向滾動(dòng)條 也可能是縱向滾動(dòng)條 scrollBottom(event) { // 1,可視區(qū)域 let clientHeight = this.listEle.clientHeight; // 2,滾動(dòng)文檔高度 let scrollHeight = this.listEle.scrollHeight; // 3,此處相等說(shuō)明:沒(méi)有縱向滾動(dòng)條 可能出現(xiàn)了橫向滾動(dòng)條 所以要忽略 if (clientHeight == scrollHeight) { return; } // 4,已滾動(dòng)的高度 let scrollTop = parseInt(this.listEle.scrollTop); // 這里 -2 是為了控制誤差 if (scrollTop + clientHeight >= scrollHeight - 2) { console.log("滾動(dòng)到底了"); // 把事件傳出去 父元素開(kāi)始請(qǐng)求下一頁(yè)的數(shù)據(jù) this.$emit("scrollBottom"); } }, }
上面代碼講解:
1,mounted里面獲取表格元素或父元素,然后監(jiān)聽(tīng)手指點(diǎn)擊事件,移動(dòng)事件,離開(kāi)事件;
2,touchstart方法中記錄坐標(biāo),touchmove使用 event.preventDefault();禁用掉原先的滑動(dòng)效果,自己通過(guò)this.listEle.scrollLeft和this.listEle.scrollTop來(lái)進(jìn)行移動(dòng);
3,注意:下面的代碼的意思是先要計(jì)算滑動(dòng)的方向,然后再進(jìn)行向?qū)?yīng)的移動(dòng),防止上下左右一起移動(dòng),導(dǎo)致移動(dòng)時(shí)太過(guò)敏感導(dǎo)致表格一直抖動(dòng);
4,scrollBottom方法是懶加載的處理 ,如果需要的話可以加上;主要是判斷上下滑動(dòng)時(shí)距離是否觸底,觸底就讓父組件請(qǐng)求數(shù)據(jù)。
最后注意:
最后需要注意的是需要使用了懶加載 :需要對(duì)父元素中的scrollBottom這個(gè)方法進(jìn)行防抖設(shè)置,因?yàn)榛瑒?dòng)到底部觸底時(shí)存在多次連續(xù)觸底的行為(比較靈敏),這樣會(huì)同時(shí)執(zhí)行多次scrollBottom這個(gè)方法;導(dǎo)致數(shù)據(jù)請(qǐng)求過(guò)多;
防抖可以使用lodash
庫(kù)里面的debounce
方法;
使用如下:在mounted中對(duì)scrollBottom進(jìn)行防抖 ,也可以對(duì)實(shí)際請(qǐng)求接口的 自定義方法 進(jìn)行防抖,
mounted(){ /* 設(shè)置接口防抖 */ this.scrollBottom= _.debounce(this.scrollBottom, 500); }
通過(guò) JavaScript 來(lái)優(yōu)化滾動(dòng)的慣性 ,這個(gè)如果小伙伴們有好的方法可以放到評(píng)論區(qū)里學(xué)習(xí)一下;
到此這篇關(guān)于vue項(xiàng)目中解決 IOS + H5 滑動(dòng)邊界橡皮筋彈性效果的文章就介紹到這了,更多相關(guān) IOS + H5 滑動(dòng)邊界橡皮筋彈性?xún)?nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
ToB項(xiàng)目如何沉淀業(yè)務(wù)公共組件示例詳解
這篇文章主要為大家介紹了ToB項(xiàng)目如何沉淀業(yè)務(wù)公共組件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10Vue3中Provide?/?Inject的實(shí)現(xiàn)原理分享
provide和inject主要為高階插件/組件庫(kù)提供用例,這篇文章主要給大家介紹了關(guān)于Vue3中Provide?/?Inject的實(shí)現(xiàn)原理,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-02-02Vue.js路由實(shí)現(xiàn)選項(xiàng)卡簡(jiǎn)單實(shí)例
這篇文章主要為大家詳細(xì)介紹了Vue.js路由實(shí)現(xiàn)選項(xiàng)卡簡(jiǎn)單實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07vue2.0移動(dòng)端滑動(dòng)事件vue-touch的實(shí)例代碼
這篇文章主要介紹了vue2.0移動(dòng)端滑動(dòng)事件vue-touch的實(shí)例代碼,需要的朋友可以參考下2018-11-11