美文网首页
小程序自定义下拉刷新上拉加载组件

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

作者: JMCC117 | 来源:发表于2018-06-12 09:39 被阅读0次
    第一次做小程序,发现微信自带的刷新加载不怎么好用,自己写了一个,做一个记录。效果图:
    
    gif5新文件.gif

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

    相关文章

      网友评论

          本文标题:小程序自定义下拉刷新上拉加载组件

          本文链接:https://www.haomeiwen.com/subject/ctwheftx.html