ios下,微信小程式scrollview元件中的fixed元素抖得和帕金森病人一樣
問題現象
這個問題是最近在優化小程式程式碼時發現的。
在ios環境下,微信小程式的scrollview元件包裹著一個position:fixed的view。
當在scrollview元件上滑動時,這個view會瘋狂抖動。
直接上圖吧:
下面是簡化後的程式碼:
<view class="main"> <scroll-view scroll-y="{{true}}" bindscroll="handleScroll" style="height:100%;" > <view> <view class="weui-navbar navbar-fixed"> 我是頭部fixed元素 </view> <view> 這裡是一大段test文字,用於佔位 </view> </view> </scroll-view> </view>
猜測與驗證
原生元件?
這個現象在沒有簡化程式碼前,我以為我是哪裡用了什麼原生元件。
因為原生元件在ios下的定位緩慢,導致了這個問題的出現。
但是當我的程式碼簡化到上面這一步的時候,發現並沒有應用原生元件。
ios下橡皮筋功能的影響?
問題在於我去掉了scroll-view後,滾動得不錯,這個頭部fixed的元素並沒有抖動。
確定是scroll-view元件下fixed元素隨著滑動就會抖動
到這一步我就確定了問題的原因,所以當然我是要先百度一下答案的。
於是我果然發現了一堆難兄難弟:ios下scroll-view中fixed元素無法固定。
貌似他們遇到的問題比我還嚴重啊,都像一條鹹魚一樣跟著滾了,而我的只是得了帕金森。
簡單場景解決方案
上面的問題還沒有官方人員回答。
不過最好的解決方案其實就是將fixed元素移出scroll-view,這個沒什麼好多說的。
元素都fixed了,沒道理還要放在scroll-view中是吧?
複雜場景解決方案
既然說了上面是簡單場景,那麼就肯定有複雜場景嘛。
我元素都fixed了,確實是沒道理要放在一個scroll-view元素中包裹著。
但是有的事就是這麼沒道理啊。
就比如我的微信小程式肯定沒有示例這麼簡單,裡面這個fixed元素不能移出去。
因為這個元素的fixed狀態並不是固定的,最開始他需要跟隨頁面一起滾動,當和頂部貼緊後,它就變成fixed了。
廢話少說,現在就說一下我的解決方案的思路:
既然要隨著頁面一起滾動,那麼肯定是要保證這個元素在scroll-view中的。
而scroll-view中的fixed元素肯定會抖,所以這個元素又一定要放在scroll-view外。
看似魚與熊掌不可兼得,實際上我們搞兩個人一人取魚,一人取熊掌就好了嘛。
我們可以在scroll-view外設定一個同樣的元素,並將其設定為fixed,並且隱藏。
當scroll-view內部的元素貼緊頂部後,將內部的元素隱藏,再顯示外部的元素即可。
以下是實現程式碼:
index.wxml:
<view class="main">
<view class="navbar navbar-fixed" hidden="{{scrollTop<=initTop}}">
我是頭部fixed元素
</view>
<scroll-view scroll-y="{{true}}" bindscroll="handleScroll" style="height:100%;" >
<view>
<view>
這裡是一大段test文字,用於佔位
</view>
<view id="navbar" class="weui-navbar navbar-fixed" hidden="{{scrollTop>initTop}}">
我是頭部fixed元素
</view>
<view>
這裡是一大段test文字,用於佔位
</view>
</view>
</scroll-view>
</view>
index.js:
Page({
data: {
initTop: 0,
scrollTop: 0,
},
onLoad: function (options) {
let query = wx.createSelectorQuery()
query.select('#navbar').boundingClientRect()
query.exec((res) => {
this.setData({
initTop: res[0].top
})
})
},
handleScroll: function (e) {
this.setData({ scrollTop: e.detail.scrollTop })
}
})
index.wxss:
.navbar-fixed {
position:fixed;
width:100%;
top:0;
left:0;
z-index:100;
}
.navbar{
height:80rpx;
line-height: 80rpx;
background:red;
text-align: center;
color: #fff;
}
隱藏BUG與修復
以上程式碼如果快速滑動是沒有問題,但是當紅色頭部元素快要貼緊頂部時慢速滑動就會出現一個很詭異的現象:
紅色頭部元素往下彈動,始終不能貼緊頂部。
而實際上不是紅色頭部元素往下彈動,而是紅色頭部元素貼緊頂部後,此時內部頭部元素隱藏,那麼scrollTop立刻變小。
因為scrollTop變小,小於了initTop,那麼內部頭部元素再次出現,於是就這樣不斷迴圈。
我們這裡需要明白hidden實際上是一個display:none的效果,所以這裡我們對內部元素的隱藏不能用hidden,而是用visibility:hidden。
這樣的話,這個內部元素就只是看不見了而已,並且頁面上顯示為背景色(這裡我們假設是白色),但是還是佔用了那麼多的空間。
那麼scrollTop就不會突然間變小,也就不會造成BUG。
同時,外部的元素會在內部元素變成白色矩形時直接出現,覆蓋在內部元素上面,那麼內部元素隱藏所造成的白色區域實際上就被外部元素遮擋住了。
當用戶在使用時,完全不會感知到內部元素這個白色區域的存在。
好了,這裡我們給出修改後的程式碼:
<view class="main">
<view class="navbar navbar-fixed" hidden="{{scrollTop<=initTop}}">
我是頭部fixed元素
</view>
<scroll-view scroll-y="{{true}}" bindscroll="handleScroll" style="height:100%;" >
<view>
<view>
這裡是一大段test文字,用於佔位
</view>
<view id="navbar" class="weui-navbar navbar-fixed" style="visibility:{{scrollTop>initTop?'hidden':'initial'}}">
我是頭部fixed元素
</view>
<view>
這裡是一大段test文字,用於佔位
</view>
</view>
</scroll-view>
</view>
總結
方法蠢是蠢了點,但是好用啊。
而且萬一哪天微信小程式修復了這個問題,咱們的方案不會出問題,替換起來也很簡單