小程序自定义下拉刷新上拉加载组件

第一次做小程序,发现微信自带的刷新加载不怎么好用,自己写了一个,做一个记录。效果图:
小程序自定义下拉刷新上拉加载组件_第1张图片
gif5新文件.gif

1.创建一个组件的文件夹scrollList。
scrollList.wxml文件代码如下:


    
        
        
    
    
        
        
            
            
                暂时没有数据
            
        
    

以自带的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;
    },
  }
})

以上就是自定义组件的全部代码了,下面是用法:


(这里写列表组件)

然后加载更多和刷新方法如下:

  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 写于,转载请注明出处,谢谢!)

你可能感兴趣的:(小程序自定义下拉刷新上拉加载组件)