下拉重新整理和上拉載入是業務上一個很常見的需求,在微信小程式裡,提供了下拉重新整理的方法 onPullDownRefresh
。而實現上拉載入相對來說就比較不方便了。
下拉重新整理
雖然微信的官方文件有很多坑,但下拉重新整理介紹的還是很全面的。在這裡稍稍帶過。
- 首先在全域性
config
中的window
配置enablePullDownRefresh
. - 在
Page
中定義onPullDownRefresh
鉤子函式。到達下拉重新整理條件後,該鉤子函式執行,發起請求方法。 - 請求返回後,呼叫
wx.stopPullDownRefresh
停止下拉重新整理。
config
config = {
pages: [
'pages/index'
],
window: {
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#ccc',
navigationBarTitleText: 'WeChat',
navigationBarTextStyle: '#000',
enablePullDownRefresh: true
}
}複製程式碼
page
onPullDownRefresh() {
wepy.showNavigationBarLoading()
setTimeout(()=>{
this.getData = '資料拿到了'
wepy.stopPullDownRefresh()
wepy.hideNavigationBarLoading()
this.$apply()
},3000)
}複製程式碼
效果如下:
你會發現下拉的過程有些僵硬。這實際上是沒有新增背景色的原因,加上背景色後再試試。
現在感覺好多了吧。下拉重新整理有現成的配置和方法,很容易實現,可上拉載入就不同了。
上拉載入
首先看一下要實現的效果,這是3g端的上拉載入。小程式要實現同樣的效果。
首先功能有
- 點選回到頂部 這個很好實現,有對應的回到頂部函式
- 滑動螢幕記錄當前頁數 這個也很好實現,主要是監聽滾動事件,判斷對應滾動條高度,去計算其與子容器的高度即可。
- 上拉載入動畫
這裡有兩個實現的方案。一個是 page
自帶的下拉觸底鉤子事件 onReachBottom
能做的只是下拉到底部的時候通知你觸底了,一個是 scroll-view
標籤自帶事件。現在用兩個方法分別實現一下上拉載入。
上拉觸底事件 onReachBottom
模板
<template>
<view class="loading"></view>
<view class="container"
@touchmove="moveFn"
@touchstart="startFn"
@touchend="endFn"
style="transform:translate3d(0,{{childTop}}px,0)">
<repeat for="{{list}}"
key="index"
index="index"
item="item">
<view>{{ item }}<text>{{index}}</text></view>
</repeat>
</view>
</template>複製程式碼
鉤子函式
data = {
getData: '',
top: 0,
lastTop: 0,
canDrag: false,
list: []
}
onReachBottom() {
this.canDrag = true
}
methods = {
moveFn(ev) {
let nowY = ev.changedTouches[0].clientY
nowY = nowY-this.lastTop
if(nowY > 0 )
this.canDrag = false
if( nowY<=0 && this.canDrag ) {
this.top = nowY
}
if( -this.top>= this.maxTop )
this.top = -this.maxTop
},
startFn(ev) {
this.lastTop = ev.changedTouches[0].clientY
},
endFn() {
if(this.top <= -this.maxTop) {
this.text = "去請求資料了"
setTimeout(()=>{
this.text = "請求回來了"
this.canDrag = false
this.list.push(...["資料","資料","資料"])
this.$apply()
this.top = 0;
return
},1000)
}
},
gotoTop() {
wepy.pageScrollTo({
scrollTop: 0
})
}
}複製程式碼
滾動容器實現上拉載入
scroll-view: 可滾動檢視區域。
它的具體用法不贅述,看官方文件就行了。這裡提解決上述問題的方法即可。
bindscrolltolower
類比原生全域性鉤子onReachBottom
模板
<scroll-view scroll-y
id="content"
@scroll="scroll"
@scrolltolower="lower"
scroll-top="{{gotoTopNum}}"
lower-threshold="100"
style="transform:translate3d(0,{{childTop}}px,0)">
<view class="sty-search"
@touchmove="moveContent"
@touchstart="startContent"
@touchend="endContent">...</view>
</scroll-view>複製程式碼
以上就是最終的模板,你可能在想為什麼這麼複雜。雖然複雜,但每個屬性都是有用的,當然這其中有幾個坑在等著我們。
首先節點分為滾動容器和子容器。
Q:為什麼滾動容器裡巢狀一個子容器,並且將拖動的三個方法繫結在它上面。
A:這是第一個坑,因為 scroll-view
容器不能繫結 touchmove
事件,那如果繫結了會怎麼樣呢?不會怎麼樣,事件鉤子不會呼叫。(這個坑在官方文件查不出來,當時繫結了不呼叫,在社群找到了解決方法,就是將touchmove事件繫結到子容器)
再來看程式碼
methods = {
async lower() {
this.canDrag = true
},
scroll (ev) {
this.scrollTop = ev.detail.scrollTop
if (ev.detail.deltaY > 0) {
this.canDrag = false
}
let nowSet = this.documentHeight+this.scrollTop-this.contentHeader
let num = Math.ceil(nowSet/this.listHeight) - 1
num = Math.floor(num / this.pageBean.pageSize) + 1
num = (num > this.pageBean.pageNo) ? this.pageBean.pageNo : num
if(num != this.page) {
this.page = num
this.$apply()
}
},
startContent(ev) {
this.lastTop = ev.changedTouches[0].clientY
if(!this.documentHeight){
this.documentHeight = wx.getSystemInfoSync().windowHeight
}
/* 這句是解決回到頂部的bug */
if (this.gotoTopNum || this.gotoTopNum==0) { this.gotoTopNum = undefined }
},
moveContent (ev) {
let {
pageNo,
pageSize,
totalCount
} = this.pageBean
let nowY = ev.changedTouches[0].clientY
nowY = nowY-this.lastTop
if (this.canDrag && nowY) {
this.state = 1;
if (nowY <= -this.maxMove) {
nowY = -this.maxMove
}
if (nowY <= 0) {
this.childTop = nowY
}
}
},
async endContent(ev) {
let {
pageNo,
pageSize,
totalCount
} = this.pageBean
if (this.childTop === -this.maxMove) {
/* 狀態 */
if (pageNo >= this.maxPage || pageNo * pageSize >= totalCount) {
this.state = 0
} else {
this.pageBean.pageNo++
await this.fillData()
this.childTop = 0
this.canDrag = false
this.$apply()
}
}
/* 如果沒超過重新整理高度則重置 */
this.childTop = 0
},
gotoTop() {
this.gotoTopNum = 0
},
}
複製程式碼
Q: 為什麼要在 touchStart
的時候 將 gotoTopNum
置為 undefined
?
A: 因為這個頁面有一個回到頂部的功能,當回到頂部時,gotoTopNum
置為0,再次下翻時,雖然實際的 scrollTop
改變了,但是 gotoTopNum
還為0,再次點選回到頂部時,因為資料未改變,檢視層就不會去更新。所以在 touchStart
的時候給 gotoTopNum
一個無效的值,再次點選回到頂部時,檢視層也就更新了。
原生滾動 OR scroll-view
對比 | 原生滾動 | scroll-view |
---|---|---|
效能 | 流暢 | 節點過多會明顯示卡頓 |
滾動函式 | onPageScroll | bindscroll |
回到頂部 | wepy.pageScrollTo(object) 預設有動畫效果,且無法取消 | 設定節點屬性 scroll-top |
坑點 | 暫時沒有發現 | 1, 與 enablePullDownRefresh , ReachBottom 不能共存 2,不能繫結touchmove事件 3,不能觸發雙擊bar欄回到頂部的“彩蛋” |
END...了嗎......
並沒有。
真機測試
實現的上拉載入在模擬器上跑的很流暢,不存在問題。可是。
如果是蘋果機的話(暫時測試iphone5 和 iPhone7),存在這樣一個問題,上拉或下拉回彈效果,這個效果會影響上拉的距離。
這個問題想了很久,目前不能優雅的解決。
所以就找產品經理修改了需求,去掉了上拉動畫效果 所以最終的效果就變成:
總結
- 在微信小程式裡操作節點是昂貴的,比在瀏覽器裡操作還昂貴(這是通過比較上拉載入功能在3g端和微信小程式的流暢度得來的),在 1.4.0 版本釋出之後,雖然給出了很多操作節點的方法,比如得到一個節點的寬高、或者通過
id
選擇器得到一個節點。請儘量減少這些方法的呼叫頻率(函式節流
)或 快取結果 - 動手之前先動腦!!!不然會走很多彎路...