小程序滚动切换页码组件

以下是基于小程序wux框架中refresh组件拓展的小程序滚动切换页码组件。
用法

<page-list  id="wux-refresher" elid="biddingList" isShowPage="{{false}}" bind:scrollbot="scrollbot"  bind:refresh="onRefresh">
       <template is="biddingList" data='{{biddingList}}'></template>   <!--列表dom-->
    </page-list>

id=“wux-refresher” 获取组件上下文
elid=“biddingList”
isShowPage="{{false}}" 是否显示页码
bind:scrollbot=“scrollbot” 触底加载
bind:refresh=“onRefresh” 上拉刷新

wxml


<scroll-view class="scroll-view" id="{{elid}}" scroll-y="{{true}}"  bindscrolltolower="scrollbot"  bindscroll="scroll" bindtouchstart="bindtouchstart" bindtouchmove="bindtouchmove" bindtouchend="bindtouchend"> 
 <view style="{{ style }}" >
    <view class="{{ classes.wrap }}">
        <view class="{{ classes.content }}">
            <view class="{{ classes.iconPulling }}">
                <text class="{{ classes.pIcon }}"></text>
            </view>
            <view class="{{ classes.textPulling }}">{{ pullingText }}</view>
            <view class="{{ classes.iconRefreshing }}">
                <text class="{{ classes.rIcon }}"></text>
            </view>
            <view class="{{ classes.textRefreshing }}">{{ refreshingText }}</view>
        </view>
    </view>

    <slot></slot>

    <view class="{{ classes.lWrap }}">
        <view class="{{ classes.lContent }}">
            <text wx:if="{{noData === false}}" class="{{ classes.rIcon }}"></text>
            <text class="wux-loader__text-loading" wx:if="{{noData === false && isShowLoadingText === true}}">{{loadingText}}</text>
            <view wx:if="{{noData === true}}">
                {{loadNoDataText}}
            </view>
        </view>
    </view>
  </view>
</scroll-view>
<view wx:if="{{isShowPage}}" style="text-align:center;">{{currentPage}}</view>

js

// componets/list/list.js
import computedBehavior from '../helpers/computedBehavior';
import classNames from '../helpers/classNames';
const defaultStyle = 'transition: transform .4s; transform: translate3d(0px, 0px, 0px) scale(1);'
Component({
  behaviors: [computedBehavior],
  options: {
    multipleSlots: true // 在组件定义时的选项中启用多slot支持
  },

  /**
   * 组件的属性列表
   */
  properties: {
    elid:{type:String,value:''},   //元素id
    isShowPage:{type:Boolean,value:false},
    isEnd:{type:Boolean,value:false},
    list:{type:Array,value:[]},
    scrollTop:{type:Number,value:0},

    prefixCls: {
      type: String,
      value: 'wux-refresher',
    },
    pullingIcon: {
      type: String,
      value: '',
    },
    pullingText: {
      type: String,
      value: '下拉刷新',
    },
    refreshingIcon: {
      type: String,
      value: '',
    },
    refreshingText: {
      type: String,
      value: '正在刷新',
    },
    disablePullingRotation: {
      type: Boolean,
      value: false,
    },
    distance: {
      type: Number,
      value: 30,
    },
    prefixLCls: {
      type: String,
      value: 'wux-loader'
    },
    isShowLoadingText: {
      type: Boolean,
      value: false
    },
    loadingText: {
      type: String,
      value: '正在加载'
    },
    loadNoDataText: {
      type: String,
      value: '没有更多数据'
    },

  },

  /**
   * 组件的初始数据
   */
  data: {
    currentPage:1,//展示的页码
    pageInfoList:{},
    initPageInfoList:{},//记录初始时第一页的数据
    pageInfoListCount:1,//有多少条元素
    scrollTop:0,

    /**下拉刷新相关 */
    style: defaultStyle,
    visible: false,
    active: false,
    refreshing: false,
    tail: false,
    lVisible: false,
    noData: false, // 是否没有更多数据
    windowHeight: 0,  // 窗口高度
    newContentHeight: 0,  // 新节点内容高度
    oldContentHeight: 0,   // 旧节点内容高度
    loading: false,   // 判断是否正在加载
  },

  computed: {
    classes() {
      const {
        prefixCls,
        pullingText,
        pullingIcon,
        disablePullingRotation,
        refreshingText,
        refreshingIcon,
        visible,
        active,
        refreshing,
        tail,
        prefixLCls,
        loading,
        noData,
      } = this.data
      const wrap = classNames(prefixCls, {
        [`${prefixCls}--hidden`]: !visible,
        [`${prefixCls}--visible`]: visible,
        [`${prefixCls}--active`]: active,
        [`${prefixCls}--refreshing`]: refreshing,
        [`${prefixCls}--refreshing-tail`]: tail,
      })  //如果是true则className push入classNames
      const content = classNames(`${prefixCls}__content`, {
        [`${prefixCls}__content--text`]: pullingText || refreshingText,
      })
      const iconPulling = classNames(`${prefixCls}__icon-pulling`, {
        [`${prefixCls}__icon-pulling--disabled`]: disablePullingRotation,
      })
      const textPulling = `${prefixCls}__text-pulling`
      const iconRefreshing = `${prefixCls}__icon-refreshing`
      const textRefreshing = `${prefixCls}__text-refreshing`
      const pIcon = pullingIcon || `${prefixCls}__icon--arrow-down`
      const rIcon = refreshingIcon || `${prefixCls}__icon--refresher`

      const lWrap = classNames(prefixLCls, {
        [`${prefixLCls}--hidden`]: !loading,
        [`${prefixLCls}--visible`]: loading,
        [`${prefixLCls}--end`]: noData,
      })
      const lContent = `${prefixLCls}__content`
      //console.log('vies',this.data.visible)
      return {
        wrap,
        content,
        iconPulling,
        textPulling,
        iconRefreshing,
        textRefreshing,
        pIcon,
        rIcon,
        lWrap,
        lContent,
      }
    },
  },

  /**
   * 组件的方法列表
   */
  methods: {
    _pageDataFormate: function(page,top,height){
      let obj = {};
      obj[page] = { scrollTop: top, scrollBottom: top + height, height: height };
      return obj
    },

    /**
     * 滑动到底部触发
     */
    scrollbot:function(e){
     // if (this.data.isEnd) return false;
      let that = this
      let view = wx.createSelectorQuery().in(this);
      
      //console.log('scrollbot count',this.data.pageInfoListCount)
      if (!this.data.loading){
        view.select("#"+this.data.elid).fields({
          scrollOffset: true
        },function(rect){
          let pageInfoList = that.data.pageInfoList;
          //当前页面有多少个元素,即多少页
          let pageInfoListCount = Object.keys(pageInfoList).length
          
          //记录上一页的bottom就是当前页的top
          pageInfoList[pageInfoListCount].scrollBottom = rect.scrollTop;

          //重新计算每一页的高度
          let height = pageInfoList[pageInfoListCount].scrollBottom - pageInfoList[pageInfoListCount].scrollTop;

          //构造当前页的数据
          let pageInfo = that._pageDataFormate(pageInfoListCount + 1,rect.scrollTop,height)
          
          //合并页
          Object.assign(pageInfoList,pageInfo);
          that.setData({
            pageInfoList,
            pageInfoListCount: Object.keys(pageInfoList).length

          })

        }).exec()
       // console.log(this.data.pageInfoList)
        this.setData({
          loading: true,
          refreshing: false,
          //oldContentHeight: newContentHeight
        })

       //this.triggerEvent('loadmore')
        this.triggerEvent('scrollbot');
      }
    },




    /**
     * 滑动时触发主要改变页码
     */
    scroll:function(e){
      //if (e.detail.scrollTop<0) return;
      //console.log('scroll',e.detail.scrollTop)
      let pageInfoList = this.data.pageInfoList;
      let scrollTop = e.detail.scrollTop;
      let currentPage = this.data.currentPage;
      let pageInfoListCount = this.data.pageInfoListCount;
    //  console.log('scrollTop',scrollTop);
     // console.log('scroll  count', pageInfoListCount)
      //判断如果当前只有一页则跳过
      if (pageInfoListCount == 1) return false;

        //如果当前滑动top大于当前页的bottom,则页码加一
      if (pageInfoList[currentPage] && scrollTop > pageInfoList[currentPage].scrollBottom){
        currentPage = (currentPage + 1) > pageInfoListCount ? pageInfoListCount : currentPage + 1
        this.setData({
          currentPage: currentPage,
          //scrollTop: scrollTop
        })
      } 
      //如果当前滑动top少于当前页的top,则页码减一
      else if (pageInfoList[currentPage] && currentPage > 1 && scrollTop < pageInfoList[currentPage].scrollTop ) {
        this.setData({
          currentPage: currentPage - 1,
          //scrollTop: scrollTop
        })
      }
      
    },



/****************下拉刷新功能************************ */
    /**
         * 显示
         */
    activate() {
      this.setData({
        style: defaultStyle,
        visible: true,
      })
    },
    /**
     * 隐藏
     */
    deactivate() {
      if (this.activated) this.activated = false

      this.setData({
        style: defaultStyle,
        visible: false,
        active: false,
        refreshing: false,
        tail: false,
      })
    },
    /**
     * 正在刷新
     */
    refreshing() {
      this.setData({
        style: 'transition: transform .4s; transform: translate3d(0, 60px, 0) scale(1);',
        visible: true,
        active: true,
        refreshing: true,

        // 刷新时重新初始化加载状态
        loading: false,
        noData: false,
        newContentHeight: 0,
        oldContentHeight: 0,
        lVisible: false,
      })
    },
    /**
     * 刷新后隐藏动画
     */
    tail() {
      this.setData({
        visible: true,
        active: true,
        refreshing: true,
        tail: true,
      })
    },
    /**
     * 加载后隐藏动画
     */
    hide() {
      this.setData({
        lVisible: false,
      })
    },
    /**
     * 正在下拉
     * @param {Number} diffY 距离
     */
    move(diffY) {
      const style = `transition-duration: 0s; transform: translate3d(0, ${diffY}px, 0) scale(1);`
      const className = diffY < this.data.distance ? 'visible' : 'active'

      this.setData({
        style,
        [className]: true,
      })
    },
    /**
     * 判断是否正在刷新
     */
    isRefreshing() {
      return this.data.refreshing
    },
    /**
     * 判断是否正在加载
     */
    isLoading() {
      return this.data.loading
    },
    /**
     * 获取触摸点坐标
     */
    getTouchPosition(e) {
      return {
        x: e.changedTouches[0].pageX,
        y: e.changedTouches[0].pageY,
      }
    },
    /**
     * 创建定时器
     */
    requestAnimationFrame(callback) {
      let currTime = new Date().getTime()
      let timeToCall = Math.max(0, 16 - (currTime - this.lastTime))
      let timeout = setTimeout(() => {
        callback.bind(this)(currTime + timeToCall)
      }, timeToCall)
      this.lastTime = currTime + timeToCall
      return timeout
    },
    /**
     * 清空定时器
     */
    cancelAnimationFrame(timeout) {
      clearTimeout(timeout)
    },
    /**
     * 下拉刷新完成后的函数
     */
    finishPullToRefresh() {
      this.setData({
        pageInfoList:this.data.initPageInfoList,
      })
      setTimeout(() => {
        this.requestAnimationFrame(this.tail)
        setTimeout(() => this.deactivate(), 200)
      }, 200)
    },
    /**
     * 上拉加载完成后的函数
     */
    finishLoadmore(bool) {
      if (bool === true) {
        setTimeout(() => {
          this.setData({
            noData: true,
            loading: false,
          })
        }, 200)
      } else {
        setTimeout(() => {
          this.setData({
            loading: false
          })
          this.requestAnimationFrame(this.hide)
          setTimeout(() => this.deactivate(), 200)
        }, 200)
      }
    },
    /**
     * 手指触摸动作开始
     */
    bindtouchstart(e) {
     // console.log('tt', this.isRefreshing() || this.isLoading())
      if (this.isRefreshing() || this.isLoading()  ) return false

      const p = this.getTouchPosition(e)

      this.start = p
      this.diffX = this.diffY = 0

      this.activate()
    },
    /**
     * 手指触摸后移动
     */
    bindtouchmove(e) {
     
     // console.log('tet', this.isRefreshing() || this.isLoading())
      if (!this.start || this.isRefreshing() || this.isLoading() ) return false

      const p = this.getTouchPosition(e)

      this.diffX = p.x - this.start.x
      this.diffY = p.y - this.start.y
     // console.log(this.diffY);
      if (this.diffY < 0) {
        return false;
      }


      
      this.diffY = Math.pow(this.diffY, 0.8);
    //  console.log(this.diffY);
      if (!this.activated && this.diffY > this.data.distance) {
        this.activated = true
        this.triggerEvent('pulling')
      } else if (this.activated && this.diffY < this.data.distance) {
        this.activated = false
      }

      this.move(this.diffY)
    },
    /**
     * 	手指触摸动作结束
     */
    bindtouchend(e) {
      this.start = false

      if (this.diffY <= 0 || this.isRefreshing() || this.isLoading()) return false
   //   console.log(this.diffY,'dd')
      this.deactivate()
      
      if (Math.abs(this.diffY) >= this.data.distance) {
        this.refreshing()
        this.triggerEvent('refresh')
      }

    },
    

  },
  


  /**
   * 生命周期
   */
  lifetimes: {
    created() {
      this.lastTime = 0
      this.activated = false
    },
    attached() {
      let that = this
      wx.getSystemInfo({
        success: function (res) {
          that.setData({
            windowHeight: res.windowHeight
          })
        }
      });
    },

    /**
     * 初始化列表高度
     */
    ready() {
      let that = this;
      let query = wx.createSelectorQuery().in(this);//初始时紧选区页面范围的节点,不会选取任何自定义组件中的组件
      
      let id = "#" + this.data.elid
      query.select(id).boundingClientRect(function (rect) {
        let pageInfoList = that._pageDataFormate(1, 0,rect.height);
        that.setData({
          pageInfoList,
          initPageInfoList:pageInfoList,
        })
      }).exec();
    },
  },
})

wxss

/*@import "../../colorui/main.wxss";
@import "../../colorui/icon.wxss";*/
.scroll-view{
  width: 100%;
  height: 100%;/*动态高度*/
}



.wux-refresher {
  position: absolute;
  top: -120rpx;
  right: 0;
  left: 0;
  overflow: hidden;
  margin: auto;
  height: 100rpx
}
.wux-refresher--hidden {
  visibility: hidden
}
.wux-refresher--visible {
  visibility: visible
}
.wux-refresher__content {
  position: absolute;
  bottom: 10rpx;
  left: 0;
  width: 100%;
  color: #666;
  text-align: center;
  font-size: 60rpx
}
.wux-refresher__content--text {
  bottom: 0
}
.wux-refresher__text-pulling {
  font-size: 32rpx;
  line-height: 32rpx
}
.wux-refresher__text-refreshing {
  font-size: 32rpx;
  line-height: 32rpx;
  display: none
}
.wux-refresher__icon-pulling {
  width: 100%;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  transform-style: preserve-3d;
  padding: 14rpx 0;
  animation-name: refresh-spin-back;
  animation-duration: .2s;
  animation-timing-function: linear;
  animation-fill-mode: none;
  transform: translateZ(0) rotate(0)
}
.wux-refresher__icon-refreshing {
  width: 100%;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  transform-style: preserve-3d;
  padding: 14rpx 0;
  display: none;
  animation-duration: 1.5s
}
.wux-refresher__icon--arrow-down {
  display: block;
  margin: 0 auto;
  width: 40rpx;
  height: 40rpx;
  background-repeat: no-repeat;
  background-position: center center;
  background-size: 40rpx 40rpx;
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAQAAAAAYLlVAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAHdElNRQfhAhQABBG6qxTZAAABVklEQVRo3u3YvUoDQRiF4ePPTZgbEGsh23sBgqWCKQe8AVvB0tJySgPah9T2CaSxkNhY6i3YaRN1J7uz+31nAmnOVJNkM++zCcyyC2hobHnslHw5HOIZA3zgJL6xa+wX8ZcAgAGW/InsFgE2MAQQQAABBBBAAAEEEEAAAQQQYOuAjtvzcIprfOImvpYEwhFucYC7OGn/PHtfH87xuJpWcZ455vt3FjPrhCFmq+lFfGo7Iv8X3P/NZmFInv1/vr6eEfBVm1OEJJ+uZwJcJa/chLX8+nr9gDjFiCc08qM49f4CiGOW0JIf547t3Ac4giffuxH5Cb68YSf0Ebx501ZsJ/jzxmuBjcDkzRejfgKXdz2sDpd4SN6oasmKyzufljcIuWHOA3sewOLl+B1nm8w7ASaCK+8G9BKceQLQSXDnKUCWQORJQCuBytOABoHMFwASAp3X0AB+AFVjfoii8X5lAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE3LTAyLTIwVDAwOjA0OjE3KzA4OjAwenpBPAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0wMi0yMFQwMDowNDoxNyswODowMAsn+YAAAAAASUVORK5CYII=)
}
.wux-refresher__icon--refresher {
  display: block;
  margin: 0 auto;
  width: 40rpx;
  height: 40rpx;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20viewBox%3D'0%200%20120%20120'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20xmlns%3Axlink%3D'http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink'%3E%3Cdefs%3E%3Cline%20id%3D'l'%20x1%3D'60'%20x2%3D'60'%20y1%3D'7'%20y2%3D'27'%20stroke%3D'%236c6c6c'%20stroke-width%3D'11'%20stroke-linecap%3D'round'%2F%3E%3C%2Fdefs%3E%3Cg%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(30%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(60%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(90%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(120%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(150%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.37'%20transform%3D'rotate(180%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.46'%20transform%3D'rotate(210%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.56'%20transform%3D'rotate(240%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.66'%20transform%3D'rotate(270%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.75'%20transform%3D'rotate(300%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.85'%20transform%3D'rotate(330%2060%2C60)'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
  background-repeat: no-repeat;
  background-position: 50%;
  background-size: 100%;
  transform-origin: 50%;
  animation: refresh-spin-rotate 1s steps(12,end) infinite
}
.wux-refresher--active.wux-refresher--refreshing {
  transition: transform .2s;
  transform: scale(1)
}
.wux-refresher--active.wux-refresher--refreshing .wux-refresher__icon-pulling,
.wux-refresher--active.wux-refresher--refreshing .wux-refresher__text-pulling {
  display: none
}
.wux-refresher--active.wux-refresher--refreshing .wux-refresher__icon-refreshing,
.wux-refresher--active.wux-refresher--refreshing .wux-refresher__text-refreshing {
  display: block
}
.wux-refresher--active.wux-refresher--refreshing .wux-refresher--refreshing-tail {
  transform: scale(0)
}
.wux-refresher--active .wux-refresher__icon-pulling:not(.wux-refresher__icon-pulling--disabled) {
  animation-name: refresh-spin;
  transform: translateZ(0) rotate(-180deg)
}
.wux-loader {
  overflow: hidden;
  margin: auto;
  height: 100rpx;
  font-size: 30rpx;
  position: relative;
  text-align: center;
  display: none
}
.wux-loader .wux-refresher__icon--refresher {
  display: inline-block;
  margin: 0
}
.wux-loader__text-loading {
  margin-left: 10rpx
}
.wux-loader--hidden {
  visibility: hidden;
  display: none
}
.wux-loader--visible {
  visibility: visible;
  display: block
}
.wux-loader--end {
  visibility: visible;
  display: block
}
.wux-loader__content {
  position: absolute;
  width: 100%;
  top: 50%;
  transform: translateY(-50%);
  color: #666;
  display: -ms-flexbox;
  display: flex;
  -ms-flex-align: center;
  align-items: center;
  -ms-flex-pack: center;
  justify-content: center
}
@keyframes refresh-spin {
  0% {
    transform: translateZ(0) rotate(0)
  }
  to {
    transform: translateZ(0) rotate(180deg)
  }
}
@keyframes refresh-spin-back {
  0% {
    transform: translateZ(0) rotate(180deg)
  }
  to {
    transform: translateZ(0) rotate(0)
  }
}
@keyframes refresh-spin-rotate {
  100% {
    transform: rotate(360deg)
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kitt15

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值