tags: [wx]
categories: [微信小程序]
cover:
—
需求:
在小程序內存在列表等形式的頁面內增加下拉刷新功能,提高用戶體驗感,加強界面操作與交互性;
實現方法:
1、小程序提供的下拉刷新(無法自定義刷新動畫)
- 在頁面設置內開啓下拉(單獨頁面設置);
{
"enablePullDownRefresh": true,
}
- 使用
onPullDownRefresh()
監聽用戶下拉操作,實現刷新操作; - 也可以通過
wx.startPullDownRefresh
和wx.stopPullDownRefresh
觸發和關閉頁面下拉刷新;
可能遇到的問題:
1)下拉時沒有出現刷新的點點動畫
可能是因爲設置的頁面背景色與點點動畫一致導致無法看到動畫,可以在頁面配置中配置背景文字顏色:
{
"backgroundTextStyle": "dark"
}
2、scroll-view內refresher-enabled屬性開啓自定義刷新
基本庫要求:2.10.1
refresher-enabled | boolean | false | 否 | 開啓自定義下拉刷新 | 2.10.1 |
---|---|---|---|---|---|
refresher-threshold | number | 45 | 否 | 設置自定義下拉刷新閾值 | 2.10.1 |
refresher-default-style | string | “black” | 否 | 設置自定義下拉刷新默認樣式,支持設置 black/white/none , none 表示不使用默認樣式 |
2.10.1 |
refresher-background | string | “#FFF” | 否 | 設置自定義下拉刷新區域背景顏色 | 2.10.1 |
refresher-triggered | boolean | false | 否 | 設置當前下拉刷新狀態,true 表示下拉刷新已經被觸發,false 表示下拉刷新未被觸發 | 2.10.1 |
bindrefresherpulling | eventhandle | 否 | 自定義下拉刷新控件被下拉 | 2.10.1 | |
bindrefresherrefresh | eventhandle | 否 | 自定義下拉刷新被觸發 | 2.10.1 | |
bindrefresherrestore | eventhandle | 否 | 自定義下拉刷新被複位 | 2.10.1 | |
bindrefresherabort | eventhandle | 否 | 自定義下拉刷新被中止 | 2.10.1 |
官方文檔:scroll-view
3、原始scroll-view自定義下拉實現(爲兼容2.10.1一下的下拉刷新
通過監聽手指移動距離控制需要下拉模塊的下拉距離,主要事件bindtouchstart,bindtouchmove和bindtouchend,bindtouchmove記錄手指開始下拉時的起始位置,bindtouchmove計算下拉距離,bindtouchend判斷並實現刷新方法。
我的自定義下拉組件(Taro框架)
import Taro, { Component, render } from "@tarojs/taro";
import { View, Image, ScrollView } from "@tarojs/components";
import { getGlobalData } from "../../state/global";
import util from "../../utils";
import "./index.less";
const rpx2px = util.rpx2px();
export default class Loading extends Component {
config = {
enablePullDownRefresh: false,
disableScroll: true
};
constructor() {
super(...arguments);
this.state = {
now_scroll_top: 0,
moveStartPosition: 0, //開始位置
moveDistance: 0, //移動距離
moveRefreshDistance: rpx2px(136), //達到刷新的閾值,沒有達到不進行刷新並回彈
moveMaxDistance: rpx2px(220), //最大可滑動距離
isRefreshMaxDown: false, //是否達到了最大距離, 用來判斷是否要震動提示
loading: false, //是否正在loading
loading_scale: 0,
// 2020.03.03 動態分成兩個階段,下拉時展示小鴨子浮動,放手後加載中鱷魚
pull_moving: false,
back_top_num: ""
};
}
componentWillReceiveProps(nextProps) {
// 父組件觸發刷新結束/取消刷新
// console.log(nextProps, this.state.loading)
if (nextProps.refreshLoading != this.state.loading) {
if(nextProps.refreshLoading) {
this.setState({
loading: nextProps.refreshLoading
});
} else {
this.stopPullLoading();
}
}
}
render() {
let {
now_scroll_top,
moveDistance,
loading_scale,
loading,
pull_moving,
back_top_num
} = this.state;
let { autoHeight } = this.props;
let sys_info = getGlobalData("system_info");
return (
<ScrollView
className={["view-component component-pull-scroll", autoHeight ? "scroll-auto" : ""]}
id="pull-scroll-view"
style={{ height: autoHeight ? 'auto' : (sys_info.screenHeight - 80 + "px") }}
scrollY={true}
scrollTop={back_top_num}
data-nowScrollTop={now_scroll_top}
onTouchStart={this.pullTouchStart.bind(this)}
onTouchMove={this.pullTouchMove.bind(this)}
onTouchEnd={this.pullTouchEnd.bind(this)}
onScroll={this.pullScroll.bind(this)}
scrollWithAnimation
>
<View
className="pull-view-container"
style={{ transform: "translateY(" + moveDistance + "px)" }}
>
<View
className="pull-refresh-box"
style={{
transform: "scale(" + loading_scale + ") translateY(-100%)"
}}
>
{(pull_moving || loading) && (
<Image
className="loading-img"
src="http://assets-cdn.lanqb.com/manual-box/mp/mp-pullLoading-duck.gif"
></Image>
)}
</View>
<slot></slot>
</View>
</ScrollView>
);
}
/**
* 觸摸事件開始
* @param e
*/
pullTouchStart(e) {
let {
moveDistance,
moveStartPosition,
loading,
now_scroll_top
} = this.state;
if (loading || now_scroll_top >= 30) return;
moveDistance = 0; //重置移動距離
moveStartPosition = e.touches[0].clientY; //記錄開始移動的位置
this.setState({
moveDistance,
moveStartPosition
});
}
/**
* 觸摸事件移動
* @param e
*/
pullTouchMove(e) {
const _this = this;
let {
loading,
moveDistance,
moveStartPosition,
moveMaxDistance,
isRefreshMaxDown,
loading_scale,
moveRefreshDistance,
pull_moving,
now_scroll_top
} = this.state;
//如果正在loading,則不進行接下來的行爲
if (loading || now_scroll_top >= 30) return;
//當前scroll-view所在的滾動位置
let nowScrollTop = e.currentTarget.dataset.nowscrolltop;
//開始計算移動距離
moveDistance = e.touches[0].clientY - moveStartPosition;
//如果是往下滑動,則阻止接下來的行爲
if (moveDistance <= 0) {
loading_scale = 0;
// _this.stopPullLoading();
} else {
// if (nowScrollTop !== 0) {
// // 如果滾動高度 !== 0 則不進行任何操作
// } else {
if (moveDistance > moveMaxDistance) {
//達到閾值
// 顯示刷新動畫
pull_moving = true;
loading_scale = 1;
loading = false;
moveDistance = moveMaxDistance;
//觸發一次震動
if (!isRefreshMaxDown) {
isRefreshMaxDown = true;
Taro.vibrateShort();
}
} else if (moveDistance < moveRefreshDistance) {
loading_scale = 0;
loading = false;
pull_moving = false;
} else {
loading = false;
pull_moving = true;
loading_scale =
moveDistance / rpx2px(136) > 1 ? 1 : moveDistance / rpx2px(136);
}
// }
}
this.setState({
moveDistance,
isRefreshMaxDown,
loading_scale,
pull_moving
});
}
/**
* 結束觸摸事件
*/
pullTouchEnd(e) {
const _this = this;
let {
loading,
moveDistance,
moveRefreshDistance,
loading_scale,
moveMaxDistance,
pull_moving,
now_scroll_top
} = this.state;
if (loading || now_scroll_top >= 30) return;
//當前scroll-view所在的滾動位置
let nowScrollTop = e.currentTarget.dataset.nowscrolltop;
//如果是往下滑動,則阻止接下來的行爲
if (moveDistance <= 0) {
// return false
loading_scale = 0;
moveDistance = 0;
loading = false;
pull_moving = false;
} else {
if (moveDistance < moveRefreshDistance) {
// 移動距離小於刷新閾值,不進行刷新
loading_scale = 0;
moveDistance = 0;
loading = false;
pull_moving = false;
console.log("需要回彈");
} else {
// 開始刷新
loading = true;
pull_moving = false;
_this.pullRefresh();
if (moveDistance >= moveMaxDistance) {
moveDistance = moveRefreshDistance;
loading_scale =
moveDistance / rpx2px(136) > 1 ? 1 : moveDistance / rpx2px(136);
}
}
// }
}
this.setState({
loading,
//重置
moveStartPosition: 0,
isRefreshMaxDown: false,
loading_scale,
moveDistance,
pull_moving
});
}
pullRefresh() {
const _this = this;
this.props.onPullRefresh();
}
pullScroll(event) {
let { scrollTop } = event.detail;
this.setState({
now_scroll_top: scrollTop,
back_top_num: ""
});
}
stopPullLoading() {
const _this = this;
setTimeout(function() {
_this.setState({
moveDistance: 0,
moveStartPosition: 0,
isRefreshMaxDown: false,
pull_moving: false,
back_top_num: 0,
now_scroll_top: 0
});
setTimeout(function() {
_this.setState({
loading_scale: 0,
loading: false,
})
}, 1500)
}, 2000)
}
}
部分問題:
1、與ios上橡皮筋效果衝突導致下拉無法觸發自定義刷新
頁面配置disabledScroll
,禁止頁面滾動,同時頁面內的列表滾動需要自己再優化調整;
2、scroll-view的scroll問題
需要設定固定高度然後縱向滾動。[我都忘了是啥問題了……
3、頁面下拉刷新結束後再滑動列表出現閃屏
在禁止頁面橡皮筋效果後,如果頁面內存在需滾動區域使用scroll-view效果比view更加流暢;
不設定固定高度不會發生閃屏但是頁面滾動非常不流暢;
4、使用官方提供的scroll-view自定義的動畫時,當scroll-view內容不足充滿一屏時下拉出現問題
將scroll-view設置固定高度後,將其子元素的高度設置多一像素達到隱形撐滿的效果。
<ScrollView
className={["view-component component-pull-scroll", autoHeight ? "scroll-auto" : ""]}
id="pull-scroll-view"
style={{ height: (sys_info.screenHeight + "px") }}
>
<View
className="pull-view-container"
style={{ height: (sys_info.screenHeight + 1 + "px") }}
>...</View>
</ScrollView>
5、scroll-view內fixed元素問題
ios內scroll-view內fixed元素層級會出現問題,可能出現被遮擋的問題。
6、當頁面內局部需要下拉刷新時可能導致內外兩個滾動條問題
一個是頁面滾動條一個是scroll-view滾動條,由於操作的時候觸發的是scroll-view部分的滾動導致頁面滾動無法進行從而影響頁面其他操作。
Q5和Q6可以合併成一個問題,當頁面需要一個吸頂操作時,即滑動距離超過閾值時導航條吸頂的功能,若scroll-view將整個頁面包含就會出現Q5的問題,可能導致在ios內吸頂的導航欄無法顯示,若scroll-view只包含需要刷新的部分則會出現Q6的兩個滾動條的問題。
1)在頁面未觸發吸頂時禁止scroll-view模塊下拉,觸發後放開滾動,同時會導致無法下拉。
2)頁面滾動觸發,scroll-view模塊可下拉,但是滾動區域無法滾動,且下拉動畫只顯示一次。