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

第一次做小程序,发现微信自带的刷新加载不怎么好用,自己写了一个,做一个记录。效果图:
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 写于简书,转载请注明出处,谢谢!)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容