微信小程序列表加载动画

  • 微信小程序列表加载动画示例
  • 实现思路
  • 什么是动画?
  • 导航栏设置
  • 列表动画加载
  • 完整代码demo


微信小程序列表加载动画示例

初学微信小程序开发,记录一下。网上找了很久这种效果,帖子很少,都没看到想要的,自力更生,翻翻官方文档自己写。

效果:

微信小程序项目动效加载中 demo 微信小程序 加载动画_微信小程序项目动效加载中 demo

实现思路

什么是动画?

接触一项技术前,阅读官方文档是必不可少的,明白了原理才能进行开发,微信开发文档上面有详细介绍    https://developers.weixin.qq.com/miniprogram/dev/api/ui/animation/wx.createAnimation.html

导航栏设置

页面的导航栏,这里推荐学习尚硅谷提供的微信小程序入门教程,针对一个高仿网抑云音乐项目来进行讲解,对萌新来说很有帮助,上面的老师讲得挺详细的,跟着去做的时候,千万别看一点做一点,最好做到统筹全局,把一个章节看完再去动手,反复看几遍,能避免踩坑。
https://www.bilibili.com/video/BV12K411A7A2?p=44
上面视频链接做了详细的导航栏切换效果的实现。

列表动画加载

首先要明确的是,需要加载的列表数据需要滚动,这样的话,需要用一个scroll-view来进行包裹,相信静态数据显示大家都不会有问题,so easy,使用wx:for来进行列表数据加载。
当静态数据搭建好后,看起来总是怪怪的,静止的画面总感觉缺了什么,不够高大上?
重头戏开始,首先,附上wxml代码。
<scroll-view scroll-x class="navScroll" enable-flex scroll-into-view="{{'scroll'+navId}}" scroll-with-animation>
  <view id="{{'scroll'+item.id}}" class="navItem" wx:for="{{navList}}" wx:key="id">
    <view class="navContent {{navId===item.id?'active':''}}" bindtap="changeNav" data-id="{{item.id}}">
      {{item.name}}
    </view>
  </view>
</scroll-view>

<view class="hr"></view>

<scroll-view scroll-y class="srollDataCard" bindrefresherrefresh="handleAnnounceRefresh"
  refresher-enabled="{{isRefresher}}" refresher-triggered="{{isTriggered}}" bindscrolltolower="handleToLower">
  <view wx:if="{{navId == 0}}" wx:for="{{announceList}}" wx:key="pk_bill" wx:for-index="id">
    <view class="announceCard" animation="{{ani[id]}}" style="opacity:{{opacity}};transform:{{transform}};" bindtap="showAnnounceDetail">
      {{item.pk_bill}}
      <!--卡片内容自行设置-->
    </view>
  </view>
</scroll-view>

<!--模拟弹窗画布-->
<view class="mask" bindtap="closeAnnounceDetail" wx:if="{{isShowModal}}"></view>

<!--模拟弹窗-->
<view class="modalDlg" animation="{{annDetailAni}}">
  <view class="annDetailcloseBtnArea">
    <button bindtap="closeAnnounceDetail" size="mini"
      style="width:60rpx;height:60rpx;line-height:60rpx;font-size:30rpx;margin-right:2rpx;display:inline;float:right;">×</button>
  </view>
</view>

scroll-view参数解析:
scroll-y:实现y轴滚动
refresher-enabled、bindrefresherrefresh:实现下拉刷新,bindrefresherrefresh绑定下拉刷新的方 法。refresher-enabled,开启刷新,boolean值。
refresher-triggered:加载数据完成让下拉刷新的三个点…消失的标记,boolean值。
bindscrolltolower:绑定了触底加载的方法。
此处有个小坑,实践证明refresher-enabled不要直接写死把它恒等于true,这样会出现初次进入界面,数据拿到了,界面无法渲染成功的问题,出现一片空白。解决方法:在初次加载界面的时候在data里初始为false,在拿到数据后再设置为true
其余参数解析:
navId :导航栏下标,这边用第一个导航栏,即下标为0。
pk_bill:数据列表的主键。
id:for循环每次执行的下标,相当于java里for循环一般这么写for(int i=0,i<2;i++) 这个id就相当于这个i
ani[id]:动画数组。
showAnnounceDetail:绑定了列表的卡片,弹出拟态窗口的方法。

JS代码

const app = getApp();
let navList = [{
  id: 0,
  name: "页面导航0"
}, {
  id: 1,
  name: "页面导航1"
}, {
  id: 2,
  name: "页面导航2"
}, {
  id: 3,
  name: "页面导航3"
}, {
  id: 4,
  name: "页面导航4"
}, {
  id: 5,
  name: "页面导航5"
}];
Page({
  /**
   * 页面的初始数据
   */
  data: {
    navList: navList, //导航栏tag选项
    navId: 0, //标识导航栏下标
    isTriggered: true, //标志下拉刷新
    announceList: [], //列表卡片列表
    isShowScroll: false, // 是否出现滚动条
    isRefresher: false, // 这一个条件很重要,如果不这样设置,数据加载会出现问题,会出现需要下拉才加载出数据
    ani: [],
    currentpage: 1,
    pagesize: 6,
    totalcount: 1,
    opacity: 0,
    transform: "",
    annDetailAni: "",
    isShowModal: false, //点击卡片详情 背景画布显示
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    console.log("已进入系统主页");
    // 当前微信用户的权限校验 加载数据区域 默认第一个导航栏页签 公告大厅
    wx.showLoading({
      title: '正在加载',
    })
    // 当前微信用户的权限校验过滤
    this.checkUserAuth();
    // 查询列表卡片
    this.queryList(1);
    // 查询总页数
    this.queryPageCount();
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {

  },

  /**
   * 当前微信用户的权限校验过滤
   */
  checkUserAuth() {
    // 权限过滤 省略
  },

  queryList(currentpage) {
    /**
     * 这里可以被视为发起请求 之后的回调操作
     * currentpage传过来是分页查询参数:当前页
     * 下面是造出来的数据 上拉触底加载即是把数据追加到list尾部
     */
    let announceList = [];
    if (currentpage == 1) {
      announceList = [{
        "pk_bill": "卡片1",
      }, {
        "pk_bill": "卡片2",
      }, {
        "pk_bill": "卡片3",
      }, {
        "pk_bill": "卡片4",
      }, {
        "pk_bill": "卡片5",
      }, {
        "pk_bill": "卡片6",
      }];
    }
    if (currentpage == 2) {
      let nextAnnounceList = [{
        "pk_bill": "卡片7",
      }, {
        "pk_bill": "卡片8",
      }];
      announceList = this.data.announceList.concat(nextAnnounceList);
    }
    // 必须在执行滑动前初始化动画数组
    this.setData({
      announceList: announceList,
      isRefresher: true,
      opacity: 0,
      transform: `translateY(${200*this.data.pagesize}rpx)` // 根据分页的数量 计算初始隐藏(透明)的位置 如果这个位置露出了当前屏幕一点点,那么动画将不会显示效果
    });
    // 设置setTimeout的时间 是因为:一般执行回调 请求会有一个时间,加载动画需要这个时间来反应,如果没有一点点延迟,那么动画加载便失效
    // 这个时间可以很短很短 短到1ms 但就是需要 如果是写在接口回调里 则去掉这个setTimeout
    setTimeout(() => {
      this.slideDown(this.data.announceList);
    }, 1);
    wx.hideLoading({})
    console.log("加载数据完成,当前数据:", JSON.stringify(this.data.announceList));
  },

  queryPageCount() {
    // 自行编写查询分页总数代码
    this.setData({
      totalcount: 2
    });
  },

  // 公告大厅加载动画
  slideDown(dataList) {
    // 关于微信小程序动画的参数 请查看官方文档
    const that = this;
    let duration = 600;
    for (let a in dataList) {
      let animation = wx.createAnimation({
        duration: duration,
        timingFunction: 'ease',
      });
      that.setAni(that, animation);
      duration = duration + 200;
    }
  },

  // 设置动画数组
  setAni(that, animation) {
    let ani = that.data.ani;
    ani.push(animation.translateY(0).opacity(1).step().export());
    that.setData({
      ani: ani
    })
    console.log("设置动画中");
  },

  /**
   * 导航点击切换事件
   */
  changeNav(e) {
    // 通过id进行传参会自动转化为String,需要进行*1处理成number  e.currentTarget.id * 1
    // 通过data-id进行传参则不会
    this.setData({
      navId: e.currentTarget.dataset.id,
    });
  },

  /**
   * 下拉刷新
   */
  handleAnnounceRefresh() {
    console.log("下拉刷新");
    wx.showLoading({
      title: '正在加载',
    })
    if (this.data.navId == 0) {
      // 重新加载须清空当前页面数据和动画数组
      this.setData({
        announceList: [],
        ani: [],
        currentpage: 1
      })
      // 重新加载第一页数据
      this.queryList(1);
      // 查询公告大厅总页数
      this.queryPageCount();
      // 此处设置setTimeout的目的是不让下拉刷新的三个... 立即消失 避免回弹太快产生错觉
      setTimeout(() => {
        this.setData({
          isTriggered: false
        });
      }, 200);
    }
    wx.hideLoading({})
  },

  /**
   * 上拉触底加载
   */
  handleToLower() {
    if (this.data.currentpage + 1 <= this.data.totalcount) {
      let currentpage = this.data.currentpage + 1;
      console.log("当前页:", currentpage);
      this.setData({
        currentpage: currentpage,
      })
      if (this.data.navId == 0) {
        // 加载数据区域 页面导航0
        wx.showLoading({
          title: '正在加载',
        })
        this.queryList(currentpage);
        setTimeout(() => {
          wx.hideLoading({})
        }, 200);
      }
    } else {
      wx.showToast({
        title: "已经到底啦!",
        icon: "none",
        duration: 1000
      })
    }
  },
  
  /**
  * 弹出卡片详情
  */
  showAnnounceDetail: function () {
    // 省略
  },

  /**
  *关闭弹窗
  */
  closeAnnounceDetail: function () {
    // 省略
  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {
  	// 隐藏返回按钮
    wx.hideHomeButton();
  },

})

上拉触底加载,其实就是把下一页的内容追加到list的尾部,然后设置动画,就很美观了。
代码里都有很多注释啦,相信聪明的你们,能看懂的!

完整代码demo

以上代码拥有了核心的逻辑思想,有了灵魂,如果想得到整体,程序员进阶从初学到放弃,工资微薄,实属不易,请各位哥哥打赏一下小弟,之后附上下载链接。