为了账号安全,请及时绑定邮箱和手机立即绑定

使用vue+better-scroll实现“上拉加载下拉刷新”功能

前言

在开发移动端的时候,基本上都会使用到上拉加载下拉刷新这个功能,下面一起来看一下具体该如何实现。

开发环境

  • vue
  • vue-cli
  • better-scroll
  • less

开发步骤一

  • 通过脚手架创建vue项目,安装better-scroll:npm install better-scroll --save-dev
  • 在项目中引入better-scroll对象import Bscroll from ‘better-scroll’
  • 安装less:npm install less less-loader --save-dev
  • 移动端适配(代码文件:index.html)
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
    <title>demo</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but s3_recharge doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
  </body>
  <script> 
    //屏幕适配
    (function (win, doc) {
      if (!win.addEventListener) return;
      var html = document.documentElement;
      function setFont() {
        var html = document.documentElement;
        var k = 740;
        html.style.fontSize = html.clientWidth / k * 100 + "px";
      }
      setFont();
      setTimeout(function () {
        setFont();
      }, 300);
      doc.addEventListener('DOMContentLoaded', setFont, false);
      win.addEventListener('resize', setFont, false);
      win.addEventListener('load', setFont, false);
      // 禁止缩放
      $('head').append('<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" />');
      document.documentElement.addEventListener('touchstart', function (event) {
        if (event.touches.length > 1) {
          event.preventDefault();
        }
      }, false);
      var lastTouchEnd = 0;
      document.documentElement.addEventListener('touchend', function (event) {
        var now = Date.now();
        if (now - lastTouchEnd <= 300) {
          event.preventDefault();
        }
        lastTouchEnd = now;
      }, false);
      // 解决ios safari无法禁止双指缩放问题
      document.addEventListener('gesturestart', function (event) {
        event.preventDefault();
      });
    })(window, document);
</script>
</html>
  • 创建重置样式,并在APP.vue文件中引入
html,
body,
#app {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  font-family: 'Microsoft YaHei UI';
  box-sizing: border-box;
  position: relative;
  z-index: -1;
}
h1,
h2,
h3,
h4,
h5,
p,
ul,
li,
button,
a {
  padding: 0;
  margin: 0;
  font-weight: normal;
}
a {
  text-decoration: none;
}
ul,
li {
  list-style: none;
}
button {
  border: none;
  outline: none
}
/* 金黄色 */
.golden {
  color: #ec9c00;
}
/* 灰色背景色 */
.graybgc {
  background-color: #eee;
}
/* 深灰色字体色 */
.graytext {
  color: #717171;
}
span {
  display: inline-block;
  vertical-align: middle;
}

开发步骤二

编写结构代码

<template>
  <div class="order-list graybgc">
    <div class="order-wrapper" ref="wrapper">
      <div
        class="order-info"
        ref="itemwrapper"
        v-show="dealList && dealList.length > 0"
      >
        <div>
          <!-- 刷新提示信息 -->
          <div class="top-tip" v-if="isPulldown">
            <span>重新加载~</span>
          </div>
          <div class="order-box" v-for="(item, index) in dealList" :key="index">
            <div class="header">
              <div class="user-info">
                <span class="game-name">王者荣耀</span>
                <span class="game-serve">李小萌-微信17区</span>
              </div>
              <div class="order-status">
                <span class="no-play-btn">去支付</span>
              </div>
            </div>
            <div class="info">
              <div class="info-img">
                <img
                  src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1583233053177&di=350747e7d50ea15f56c08a0d1441fb01&imgtype=0&src=http%3A%2F%2Fphotocdn.sohu.com%2F20110814%2FImg316287483.jpg"
                  alt=""
                />
              </div>
              <div class="info-number">
                <div class="title">
                  每日登录礼包
                </div>
                <div class="money graytext">
                  单价:¥20 / 件
                </div>
                <div class="number graytext">数量:10 件</div>
              </div>
            </div>
            <div class="footer">
              <div class="time">
                <span class="date">02-10 22:00</span>
              </div>
              <div class="total-money">
                <span class="text">合计:</span>
                <div class="money">
                  <span class="min"></span>
                  <span>200</span>
                </div>
              </div>
            </div>
          </div>
          <div class="null-box"></div>
          <div class="bottom-tip" v-if="isPullup">
            {{ canPullup ? "松手加载更多~" : "没有更多数据了~" }}
          </div>
          <div class="null-box"></div>
        </div>
      </div>
      <div class="order-no" v-if="!dealList || dealList.length == 0">
        <div class="img"></div>
        <div class="desc">暂时还没您的购买记录哦</div>
      </div>
    </div>
    <div class="order-footer">
      当前页面仅提供14天记录查询,如有疑问,请
      <a href="#">联系客服</a>
    </div>
  </div>
</template>

注意下面几个问题:

  • 高度问题:当子元素高度小于父元素高度时,不会触发任何下拉行为。那么此时只要控制子元素的高度大于父元素的高度即可。
  • better-scroll不能滚动问题,pc端可以滚动,切换到手机端不能滚动:创建ref="wrapper"这个BScroll对象时,就会把子元素的ovflow:scroll给禁止掉,此时我们重新设置子元素的相关属性即可。详解地址

开发步骤二

编写样式代码

<style scoped lang="less">
.order-list {
  position: relative;
  width: 100%;
  min-height: 100%;
  padding: 0.25rem 0.4rem 0;
  box-sizing: border-box;
  .order-wrapper {
    height: 90vh;
    .order-info {
      width: 100%;
      height: 91.1vh;
      overflow-y: scroll;
      -webkit-overflow-scrolling: touch;
      .top-tip {
        margin-top: .2rem;
        width: 100%;
        height: 0.5rem;
        line-height: 0.1rem;
        text-align: center;
        span {
          font-size: 0.2rem;
        }
      }
      .order-box {
        background-color: #fff;
        padding: 0.15rem 0.3rem;
        box-sizing: border-box;
        border-radius: 0.05rem;
        font-size: 0.2rem;
        margin-bottom: 0.3rem;
        .header {
          display: flex;
          justify-content: space-between;
          align-items: center;
          .user-info {
            color: #000;
            text-align: left;
            .game-name {
              margin-right: 0.1rem;
            }
          }
          .order-status {
            .no-play-btn {
              padding: 0.01rem 0.2rem;
              background-color: #ec6f00;
              color: #fff;
              border-radius: 0.2rem;
              margin-left: 0.1rem;
            }
          }
        }
        .info {
          display: flex;
          width: 100%;
          padding: 0.1rem 0 0.15rem;
          box-sizing: border-box;
          border-bottom: 0.02rem solid #d5d5d5;
          .info-img {
            display: inline-block;
            vertical-align: top;
            width: 1.1rem;
            height: 1.1rem;
            background-color: #ec6f00;
            text-align: center;
            line-height: 1.1rem;
            margin-right: 0.2rem;
            position: relative;
            img {
              position: absolute;
              left: 50%;
              top: 50%;
              transform: translate(-50%, -50%);
              display: inline-block;
              width: 0.8rem;
              height: 0.8rem;
              border-radius: 50%;
            }
          }
          .info-number {
            display: inline-block;
            vertical-align: top;
            height: 1.1rem;
            text-align: left;
            padding: 0;
            margin: 0;
            .title {
              font-size: 0.28rem;
              color: #000;
            }
          }
        }
        .footer {
          display: flex;
          justify-content: space-between;
          align-items: center;
          padding-top: 0.15rem;
          .money {
            display: inline-block;
            vertical-align: middle;
            font-size: 0.28rem;
            color: #000;
            span {
              vertical-align: baseline;
            }
            .min {
              font-size: 0.22rem;
            }
          }
        }
      }
      .null-box {
        width: 100%;
        height: 1px;
      }
      .bottom-tip {
        text-align: center;
        font-size: 0.2rem;
        color: #999;
        margin-top: -0.2rem;
        margin-bottom: 0.3rem;
      }
    }
    .order-no {
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%) !important;
      width: 4rem;
      height: 5rem;
      text-align: center;
      .img {
        width: 1.38rem;
        height: 1.8rem;
        background-color: #999;
        margin: 0 auto;
      }
      .desc {
        margin-top: 0.4rem;
        font-size: 0.3rem;
      }
    }
  }
  .order-footer {
    width: 100%;
    height: 0.6rem;
    line-height: 0.6rem;
    position: fixed;
    bottom: 0;
    left: 0;
    font-size: 0.24rem;
    background-color: inherit;
    z-index: 1;
    text-align: center;
    a {
      text-decoration: underline;
      color: #000;
    }
  }
}
</style>

开发步骤三

编写js代码

<script>
import BScroll from "better-scroll";
export default {
  name: "OrderList",
  data() {
    return {
      isPulldown: false,
      isPullup: false,
      pageIndex: 1,
      dealList: [], //订单记录列表
      canPullup: true,
      lock: false //避免同一时间多次请求数据
    };
  },
  created() {
    this.getOrderList();
    this.$nextTick(() => {
      this.scorllEvent();
    });
  },
  methods: {
    // 下拉刷新上拉加载
    scorllEvent() {
      this.scroll = new BScroll(this.$refs.wrapper, {
        probeType: 1,
        click: true
      });
      this.childScroll = new BScroll(this.$refs.itemwrapper, { // 解决在真机中无法滚动问题
        mouseWheel: true,
        scrollY: true,
        click: true
      });
      // 滑动过程中事件
      this.scroll.on("scroll", pos => {
        if (this.lock) return;
        if (pos.y > 40) {
          this.isPulldown = true;
        } else if (pos.y < -45) {
          this.isPullup = true;
        }
      });
      //滑动结束松开事件
      this.scroll.on("touchEnd", pos => {
        if (this.lock) return;
        if (pos.y > 40 && this.isPulldown) {
          //下拉刷新
          this.pageIndex = 1;
          this.getOrderList("down");
        } else if (pos.y < this.scroll.maxScrollY - 45 && this.isPullup) {
          //上拉加载
          if (this.canPullup) {
            this.pageIndex++;
            this.getOrderList("up");
          }
        }
      });
    },
    // 获取订单列表数据
    getOrderList(status) {
      let data = [1, 2, 3]; //模拟接口返回的数据
      if (data && data.length > 0) {
        if (status == "up") {
          this.dealList = this.dealList.concat(data);
        } else {
          this.dealList = data;
        }
      } else {
        if (status == "up") {
          this.canPullup = false;
          return;
        }
      }
      if (status) {
        this.lock = true;
        this.scroll.refresh(); // 刷新列表后,重新计算滚动区域高度
        this.dataTransition(status);
      }
    },
    // 数据过渡效果
    dataTransition(status) {
      setTimeout(() => {
        this.isPullup = false;
        this.isPulldown = false;
        this.refreshalert(status);
      }, 1000);
    },
    // 数据刷新/加载成功
    refreshalert(status) {
      setTimeout(() => {
        alert(`${status == "down" ? "刷新成功" : "加载成功"}`);
        this.lock = false;
      }, 1200);
    }
  }
};
</script>

尾声

以上便是实现上拉加载下拉刷新功能的详细过程。更高阶的技巧是将该功能封装成一个公共的组件,提供给各个页面使用,这个过程的实现且看下回分解。

点击查看更多内容
2人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
Web前端工程师
手记
粉丝
114
获赞与收藏
415

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消