🌾 用支付宝原生框架写一个下拉刷新组件。
背景
支付宝提供的my.startPullDownRefreshAPI是下拉整个页面的,当有固定头部的时候,就达不到要求。而MAS提供的adc-flat-list组件可以支持固定头部,但是不能支持有底部,如果将底部放在组件外,就会出现两个滚动条。 后来参考了些博客,整理了这个满足业务需求的下拉刷新组件,参考文章最后贴出。
实现效果
子组件
组件命名为pull-refresh
<view class="pull-refresh-cont">
<!-- 插槽 - 固定的头部 -->
<slot name="header" />
<scroll-view class="scroll-view" scroll-y='{{!isindrag}}' onScroll='scorll'>
<view class='column' onTouchStart='start' onTouchEnd='end' onTouchMove='move'>
<view a:if="{{refreshBatch}}">
<view style='height:{{hei}}px;' class='refresh'>{{desc}}</view>
</view>
<!-- 插槽 - 列表内容 -->
<slot />
<!-- 插槽 - 跟在列表后面的底部内容 -->
<slot name="footer" />
</view>
</scroll-view>
</view>
.pull-refresh-cont {
display: flex;
flex-direction: column;
box-sizing: content-box;
// 兼容底部安全距离
height: calc(100vh - constant(safe-area-inset-bottom)); /* 兼容 iOS < 11.2 */
height: calc(100vh - env(safe-area-inset-bottom)); /* 兼容 iOS >= 11.2 */
overflow: hidden;
background-color: #fff;
.scroll-view {
flex: 1;
.column {
height: 100%;
.refresh {
background-color: #f5f5f5;
text-align: center;
padding: 10rpx 0;
}
}
}
}
import { createComponent } from '@whale.io/mini/es/miniu';
interface IProps {
onRefresh: () => Promise<unknown>;
}
interface Data {
desc?: string;
hei: number;
scrolltop: number;
isindrag?: boolean;
refreshBatch?: boolean;
sy: number;
}
Component(
createComponent<IProps, Data>({
props: {
onRefresh: () => new Promise<void>(executor => executor())
},
data: {
desc: '🦘下拉刷新',
hei: 0,
scrolltop: 0,
isindrag: false,
sy: 0,
refreshBatch: false
},
methods: {
saveRef (ref) {
this.$headerRef = ref;
},
start (e) {
this.data.sy = e.touches[0].clientY;
},
move (e) {
const delta = e.touches[0].clientY - this.data.sy;
if (this.data.hei <= 0 && delta <= 0) {
return;
}
if (this.data.scrolltop <= 0) {
if (this.data.isindrag === false) {
this.setData({
isindrag: true,
refreshBatch: true
});
}
let tempdelta = 0;
if (delta > 0) {
if (this.data.hei > 50) {
this.setData({
desc: '🙆?♀?松开刷新'
});
tempdelta = this.data.hei + delta / (this.data.hei - 50);
} else {
this.setData({
desc: '🦘下拉刷新'
});
tempdelta = this.data.hei + delta;
}
} else {
tempdelta = this.data.hei + delta;
if (tempdelta <= 0) {
tempdelta = 0;
}
this.setData({
desc: '🦘下拉刷新'
});
}
this.setData({
hei: tempdelta
});
}
this.data.sy = e.touches[0].clientY;
},
end () {
this.setData({
refreshBatch: false
});
if (this.data.hei >= 50) {
this.setData({
desc: '😆正在刷新...',
hei: 50,
refreshBatch: true
});
this.props.onRefresh().then(() => {
this.setData({
desc: '🌻刷新成功'
});
this.reset();
}).catch(() => {
this.setData({
desc: '😭刷新失败'
});
this.reset();
});
} else {
this.data.sy = 0;
this.setData({
desc: '🦘下拉刷新',
hei: 0,
isindrag: false,
scrolltop: 0
});
}
},
scorll (e) {
const st = e.detail.scrollTop;
if (this.data.isindrag === false) {
this.setData({
scrolltop: st
});
}
},
reset () {
setTimeout(() => {
this.data.sy = 0;
this.setData({
desc: '🦘下拉刷新',
hei: 0,
isindrag: false,
scrolltop: 0,
refreshBatch: false
});
}, 500);
}
}
})
);
父组件
在页面test中引用pull-refresh组件
<pull-refresh
onRefresh="onRefresh"
>
<view slot="header" style="backgroundColor:pink;">
欢迎使用XXX系统!
</view>
<view a:if="{{list && list.length !== 0}}">
<view a:for="{{list}}">
{{item}}
</view>
</view>
<view a:else>
lalalalalal暂无数据
</view>
<view slot="footer" style="textAlign:center;marginTop:50rpx;">
-- 我是有底线的 --
</view>
</pull-refresh>
{
"defaultTitle": "",
"usingComponents": {
"pull-refresh": "../../components/pull-refresh/index"
}
}
import { createPage } from '@whale.io/mini/es/miniu';
interface Data {
list: string[];
}
interface This {
getData: (flag?: boolean) => Promise<Record<string, unknown>[] | string[]>;
onRefresh: () => void;
}
Page(createPage<Data, This>({
data: {
list: []
},
onLoad () {
const list: string[] = [];
for (let i = 0; i < 50; i++) {
list.push(`data--${i}`);
}
this.setData({
list
});
},
getData (flag = false) {
return new Promise((resolve, reject) => {
const list: string[] = [];
my.showToast({
content: '正在加载'
});
if (flag) {
for (let i = 0; i < 100; i++) {
list.push(`新data--${i}`);
}
setTimeout(() => {
this.setData({
list
});
resolve(list);
my.hideToast();
}, 2000);
} else {
setTimeout(() => {
this.setData({
list
});
my.hideToast();
reject(new Error('报错啦'));
}, 2000);
}
});
},
async onRefresh () {
const res = await this.getData(true);
return res;
}
}));
【参考】 https://blog.51cto.com/u_15102934/2632425 https://www.it610.com/article/1282381264719986688.htm https://blog.csdn.net/zxj000830/article/details/115916757
|