第一次做小程序,发现微信自带的刷新加载不怎么好用,自己写了一个,做一个记录。效果图:
1.创建一个组件的文件夹scrollList。
scrollList.wxml文件代码如下:
<scroll-view
style="height:{{height}}"
scroll-y="true"
lower-threshold="100"
enable-back-to-top="true"
class="tloader state-{{loaderState}}"
bindscroll="onScroll"
bindscrolltolower="isEnd"
bindtouchstart="touchStart"
bindtouchend="touchEnd">
<view class="tloader-symbol">
<view class="tloader-msg"><text/></view>
<view class="tloader-loading"><text class="ui-loading"/></view>
</view>
<view
class="tloader-body"
bindtouchmove="touchMove"
style="transform: translate3D(0,{{pullDownHeight+'px'}},0)">
<slot wx:if="{{!isEmpty}}"></slot>
<view class="empty" wx:else>
<view class="icon-empty"/>
<view>
<text>暂时没有数据</text>
</view>
</view>
</view>
</scroll-view>
以自带的scroll-view组件为基础。
scrollList.wxss:
.tloader-msg:after {
content: '下拉刷新';
}
.state-reset .tloader-msg:after {
content: '';
}
.state-pulling.enough .tloader-msg:after {
content: '松开刷新';
}
.state-refreshed .tloader-msg:after {
content: '刷新成功';
}
.tloader-loading:after {
content: '正在加载...';
}
.tloader-symbol .tloader-loading:after {
content: '正在刷新...';
}
.tloader-btn:after {
content: '点击加载更多';
}
.tloader {
position: relative;
overflow-y: scroll;
}
.tloader.state-pulling {
overflow-y: hidden;
}
.tloader-symbol {
position: absolute;
top: 0;
left: 0;
right: 0;
color: #7676a1;
text-align: center;
height: 71.5px;
background-color: #EFEFF4;
overflow: hidden;
}
.state- .tloader-symbol,
.state-reset .tloader-symbol {
height: 0;
}
.state-reset .tloader-symbol {
transition: height 0s 0.2s;
}
.state-loading .tloader-symbol {
display: none;
}
.tloader-msg {
line-height: 60px;
font-size: 12px;
}
.state-pulling .tloader-msg text {
display: inline-block;
font-size: 2em;
margin-right: .6em;
vertical-align: middle;
height: 1em;
border-left: 1px solid;
position: relative;
transition: transform .3s ease;
}
.state-pulling .tloader-msg text:before,
.state-reset .tloader-msg text:before,
.state-pulling .tloader-msg text:after,
.state-reset .tloader-msg text:after {
content: '';
position: absolute;
font-size: .5em;
width: 1em;
bottom: 0px;
border-top: 1px solid;
}
.state-pulling .tloader-msg text:before,
.state-reset .tloader-msg text:before {
right: 1px;
transform: rotate(50deg);
transform-origin: right;
}
.state-pulling .tloader-msg text:after,
.state-reset .tloader-msg text:after {
left: 0px;
transform: rotate(-50deg);
transform-origin: left;
}
.state-pulling.enough .tloader-msg text {
transform: rotate(180deg);
}
.state-refreshing .tloader-msg {
height: 0;
opacity: 0;
}
.state-refreshed .tloader-msg {
opacity: 1;
transition: opacity 1s;
}
.state-refreshed .tloader-msg text {
display: inline-block;
box-sizing: content-box;
vertical-align: middle;
margin-right: 10px;
font-size: 20px;
height: 1em;
width: 1em;
border: 1px solid;
border-radius: 100%;
position: relative;
}
.state-refreshed .tloader-msg text:before {
content: '';
position: absolute;
top: 3px;
left: 7px;
height: 12px;
width: 5px;
border: solid;
border-width: 0 1px 1px 0;
transform: rotate(40deg);
}
.tloader-body {
margin-top: -1px;
padding-top: 1px;
}
.state-refreshing .tloader-body {
transform: translate3d(0, 60px, 0);
transition: transform 0.2s;
}
.state-reset .tloader-body {
transition: transform 0.2s;
}
.state-refreshing .tloader-footer {
display: none;
}
.tloader-footer .tloader-btn {
color: #484869;
font-size: .9em;
text-align: center;
line-height: 60px;
}
.state-loading .tloader-footer .tloader-btn {
display: none;
}
.tloader-loading {
display: none;
text-align: center;
line-height: 60px;
font-size: 12px;
color: #7676a1;
}
.tloader-loading .ui-loading {
font-size: 20px;
margin-right: .6rem;
}
.state-refreshing .tloader-symbol .tloader-loading,
.state-loading .tloader-footer .tloader-loading {
display: block;
}
@keyframes circle {
100% {
transform: rotate(360deg);
}
}
.ui-loading {
display: inline-block;
vertical-align: middle;
font-size: 1.5rem;
width: 1em;
height: 1em;
border: 2px solid #9494b6;
border-top-color: #fff;
border-radius: 100%;
animation: circle .8s infinite linear;
}
.empty{
color: #666;
text-align: center;
margin: 0 auto;
padding: 100rpx 100rpx;
background-color: #f5f5f5;
}
.icon-empty{
width: 120rpx;
height: 120rpx;
display: inline-block;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAABACAYAAACndwGZAAABdUlEQVR4Xu2bwU3DQBBFZ4SbgAIoACiABjhZWrsBQgE0QBtJB163QAmkARqgCeRFzgFBnHX0Eafd56PXPvznN39Odstc4zg+p5Qecuel3A8h3J/K4rmAwzDs3P2xFAC5HCGEkwwA8wcw8xjdlm5M13Uv0iiVDuRcvuwonXux9PNfYGKMm5TSVemhc/maptm2bfsxnx+DeauhV1Y+/F0IYQ+YJaEsmK2ZXdc6StM0PfV9/74wplYgrGvhy7OuM7A8xvhpZhcCzCoencGkKpKKIQGzMko/jfne4yLgIh4fhmHv7jeHdX00SoABzFJyjMkMPmAAo+0EjMEYjNEIYIzGi47BGIzRCGCMxouOwRiM0QhgjMaLjsEYjNEIYIzGi47BGIzRCGCMxouOwRiM0QhgjMaLjsEYjNEIYIzGi47BGIzRCGCMxouOwRiM0QhgjMaLjsGY/zNml1I6/EVa4+XuGzO7nLPz90nGAMCsgHk1s6bG0VnL/AW4jKZ4roy8ugAAAABJRU5ErkJggg==) no-repeat;
background-size: 100% 100%;
}
scrollList.js
const STATS = {
init: '',
pulling: 'pulling',
enough: 'pulling enough',
refreshing: 'refreshing',
refreshed: 'refreshed',
reset: 'reset',
loading: 'loading'
}
Component({
data: {
onRefresh: true,
loaderState: STATS.init,
pullHeight: 0,
progressed: 0,
pullDownHeight: 0,
scrollTop: 0,
animate: {}
},
properties: {
height: {
type: String
},
alreadyLoadData: {
type: Boolean,
value: true,
observer: function(e){
this.isChange(e)
}
},
isEmpty: {
type: Boolean,
value: false
}
},
methods:{
isChange: function(e){
if(e){
this.setData({
loaderState: STATS.refreshed
})
setTimeout(() => {
this.setData({
loaderState: STATS.reset,
pullDownHeight: 0
}, this.initSTATS)
}, 500);
}
},
initSTATS: function(){
setTimeout(() => {
this.setData({
loaderState: STATS.init
})
}, 500);
},
onScroll: function(e){
this.setData({
scrollTop: e.detail.scrollTop
})
},
isEnd: function(){
this.triggerEvent('loadMore')
},
calculateDistance: function(touch){
return touch.clientY - this._initialTouch.clientY;
},
touchStart: function(e){
if (!this.canRefresh()) return;
if (e.touches.length == 1){
this._initialTouch = {
clientY: e.touches[0].clientY,
scrollTop: this.data.scrollTop
};
}
},
touchMove: function(e){
if (!this.canRefresh() || this.data.scrollTop > 0) return;
var distance = this.calculateDistance(e.touches[0]);
if (distance > 0 && this.data.scrollTop <= 5) {
var pullDistance = distance - this._initialTouch.scrollTop;
if (pullDistance < 0) {
pullDistance = 0;
this._initialTouch.scrollTop = distance;
}
var pullHeight = this.easing(pullDistance);
this.setData({
loaderState: pullHeight > 60 ? STATS.enough : STATS.pulling,
pullDownHeight: pullHeight
});
}
},
touchEnd: function(e){
if (!this.canRefresh()) return;
if (this.data.ifScroll > 0) return;
var endState = {
loaderState: STATS.reset,
pullDownHeight: 0
};
if (this.data.loaderState == STATS.enough) {
this.setData({
loaderState: STATS.refreshing,
});
setTimeout(() => {
this.triggerEvent('onRefresh')
}, 300);
} else {
this.setData(endState)
}
},
easing: function(distance){
// t: current time, b: begInnIng value, c: change In value, d: duration
var t = distance;
var b = 0;
var d = 170; // 允许拖拽的最大距离
var c = d / 2.5; // 提示标签最大有效拖拽距离
return c * Math.sin(t / d * (Math.PI / 2)) + b;
},
canRefresh: function(){
let { onRefresh, loaderState} = this.data
return onRefresh && [STATS.refreshing, STATS.loading].indexOf(loaderState)<0;
},
}
})
以上就是自定义组件的全部代码了,下面是用法:
<list
alreadyLoadData="{{alreadyLoadData}}"
height="100%"
bindloadMore="loadMore"
bindonRefresh="onRefresh">
(这里写列表组件)
</list>
然后加载更多和刷新方法如下:
onRefresh: function () {
this.setData({
alreadyLoadData: false,
pageIndex: 1,
pageCount: 1,
})
this.loadData()
.then(res => {
this.setData({
alreadyLoadData: true
})
})
.catch(error => {
this.setData({
alreadyLoadData: true
})
})
},
loadMore: function () {
let { pageIndex, pageCount } = this.data
if (pageIndex > pageCount || this.posting) return
this.posting = true
this.loadData(true)
.then(res => {
this.posting = false
})
},
(JMCC117 2018-06-11 写于简书,转载请注明出处,谢谢!)