vue实现下拉刷新和上拉加载

本文介绍了如何在Vue应用中实现下拉刷新和上拉加载功能。通过子组件向父组件传递回调函数,利用滚动事件结合scrollTop、clientHeight和scrollHeight属性判断滚动条位置,当满足条件时触发加载新数据。回调函数返回Promise来控制组件的加载状态,确保用户体验流畅。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

子组件向父组件传入回调函数,在下拉刷新和上拉加载时候调用,函数返回promise决定刷新组件的表现状态。

判断滚动条到底部,需要用到DOM的三个属性值,即scrollTop、clientHeight、scrollHeight。

scrollTop为滚动条在Y轴上的滚动距离。

clientHeight为内容可视区域的高度。

scrollHeight为内容可视区域的高度加上溢出(滚动)的距离。

从这个三个属性的介绍就可以看出来,滚动条到底部的条件即为scrollTop + clientHeight == scrollHeight。

代码如下(兼容不同的浏览器)。

<template>
<!-- 下拉刷新 上拉加载更多-->
  <div class="container-wrapper"
    @touchstart="startTouch"
    @touchmove="move"
    @touchend="endTouch"
    ref="container"
  >
    <div class="content" ref="content">
      <!-- 下拉显示 -->
      <div class="status" :style="{'height': marginTop/50 + 'rem'}">
        <div class="msg" :style="{'line-height': height/50  + 'rem'}">
          <img src="@/assets/img/loading.png" class="icon" :class="loading !== 2 ? 'stop' : ''">
          {{msg}}
        </div>
      </div>
      <!-- 显示的数据 -->
      <slot name="main" />
      <!-- 上拉显示 -->
      <div class="status" v-show="gesture === 2" :style="{'height': height / 50 + 'rem'}">
        <div class="msg" :style="{'line-height': height / 50 + 'rem'}">
          <img src="@/assets/img/loading.png" class="icon">
          {{msg}}
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  name: 'pull-refresh',
  data () {
    return {
      loading: 0, // 是否正在加载 0-提示下拉更新,1-提示松开更新,2-加载中,(3- 暂无更新内容 4 - 更新结束)
      touchStart: 0, // 触摸起始点
      distance: 0, // 手指滑动距离
      timer: 0,
      gesture: 0,
      marginTop: 104, // 下拉的最大长度
      height: 44, // 最小长度
      toRem: 50 // 转为rem
    }
  },
  props: {
    refreshNext: {
      type: Function
    },
    loadMoreNext: {
      type: Function
    }

  },
  computed: {
    msg () {
      if (this.loading === 0) {
        return '拉动更新'
      } else if (this.loading === 1) {
        return '松开更新'
      } else if (this.loading === 2) {
        return '加载中...'
      } else if (this.loading === 3) {
        return '暂无更新内容'
      } else if (this.loading === 4) {
        return '刷新成功'
      } else {
        return '更新失败'
      }
    }
  },
  mounted () {
    this.initData()
  },
  methods: {
    initData () {
      const $content = this.$refs.content
      $content.style.marginTop = -1 * this.marginTop / 50 + 'rem'
      $content.style.transition = 'none'
      this.loading = 0
      this.touchStart = 0
      this.distance = 0
      this.gesture = 0
    },
    startTouch (e) {
      const clientY = e.targetTouches[0].clientY
      if (this.loading === 0) {
        this.touchStart = clientY
      }
    },
    move (e) {
      const $container = this.$refs.container
      const $content = this.$refs.content
      const distance = e.targetTouches[0].clientY - this.touchStart // 滑动距离
      const scrollTop = $container.scrollTop
      const clientHeight = $container.clientHeight
      const scrollHeight = $container.scrollHeight
      console.log()
      if (scrollTop === 0 && this.loading !== 2 && distance > 0) { // 页面没有下滑 并且数据没有更新 并且是下拉
        this.distance = distance
        this.gesture = 1
        if (this.distance < this.marginTop) {
          $content.style.marginTop = (this.distance - this.marginTop) / 50 + 'rem'
          if (this.distance >= this.height) {
            this.loading = 1
          }
        }
      } else if (scrollTop + clientHeight === scrollHeight && this.loading !== 2 && distance < 0) { // 上拉 // 除法有误差,设置误差范围0.4rem
        // 上拉加载
        this.gesture = 2
        this.loading = 1
      }
    },
    endTouch (e) {
      // 松开判断loading
      if (this.loading === 1) {
        this.loading = 2 // 加载中
        // 根据手势判断上拉还是下拉
        if (this.gesture === 1) {
          const $content = this.$refs.content
          $content.style.marginTop = (-1 * this.marginTop + this.height) / 50 + 'rem'
          this.refreshNext().then(res => {
          // 加载成功,判断是否有更新内容 to3 or 4
            const random = ~~(Math.random() * 100) % 2
            if (random) {
              this.loading = 3
            } else {
              this.loading = 4
            }
            this.backToTop(500)
          }).catch(_ => {
            this.loading = 5
            this.backToTop(500)
          })
        } else if (this.gesture === 2) {
          this.loadMoreNext().then(res => {
          // 加载成功,判断是否有更新内容 to3 or 4
            const random = ~~(Math.random() * 100) % 2
            if (random) {
              this.loading = 3
            } else {
              this.loading = 4
            }
            this.initData()
          }).catch(_ => {
            this.loading = 5
            this.initData()
          })
        }
      } else {
        if (this.gesture === 1) {
          this.backToTop(0)
        }
      }
    },
    backToTop (duration) {
      if (this.timer) {
        clearTimeout(this.timer)
      }
      const _this = this
      const $content = _this.$refs.content
      $content.style.marginTop = -1 * this.marginTop / 50 + 'rem'
      $content.style.transition = 'margin-top .2s .8s ease-in'
      this.timer = setTimeout(function () {
        _this.initData()
      }, 1000)
    }
  },
  beforeDestroy () {
    if (this.timer) {
      clearTimeout(this.timer)
    }
  }
}
</script>
<style lang="less" scoped>
@import '~@/assets/less/func';
.container-wrapper {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch; // 解决h5苹果ios系统中overflow: auto滑动不流畅
  .content {
    .status {
      position: relative;
      background-color: #fff;
      .msg {
        position: absolute;
        bottom: 0;
        left: 0;
        width: 100%;
        text-align: center;
        color: rgb(99, 99, 99);
        .icon {
          display: inline-block;
          vertical-align: text-bottom;
          margin-right: 5*@unit;
          height: 20*@unit;
          width: 20*@unit;
          animation: rotate .75s linear infinite;
          &.stop {
            animation: none;
          }
        }
      }
    }
  }
  @keyframes rotate {
    from {
      transform: rotate(0)
    }
    to {
      transform: rotate(360deg)
    }
  }
}
</style>

资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值