微信小程序实现骨架屏和图片懒加载

前言概述

由于常用的getBoundingClientRect()会导致页面重排和滚动事件频发触发,所以本文介绍的是使用IntersectionObserver对象实现图片懒加载效果;并在显示图片前增加骨架屏来提高体验。

Tips:

骨架屏是页面的一个空白版本,通常会在页面完全渲染之前,通过一些灰色的区块大致勾勒出轮廓,待数据加载完成后,再替换成真实的内容;

懒加载简单说就是在渲染页面时,不在视图范围内的不加载,只有元素出现在视图范围内了再渲染。

效果如下:

实现过程

一、懒加载功能

1. 文件imgsList.wxml和imgsList.wxss代码如下,这一块比较简单,可自行查看,不做过多分析;

<view class="imgs-box">
  <block wx:for="{
   
   {imgs}}" wx:key="id">
    <image class="img-{
   
   {index}}" src="{
   
   {item.showState? item.src: ''}}"></image>
  </block>
</view>
.imgs-box image {
  width: 100%;
  height: 500rpx;
}

2. 文件imgsList.js存放所有功能的逻辑代码,代码实现如下:

页面加载时(onLoad),遍历所有图片数据(imgs),只要intersectionRatio大于0(即该图片元素出现在视图中),就局部setData该图片数据显示。

Page({
  /**
   * 页面的初始数据
   */
  data: {
    imgs: [{
        id: 0,
        src: '../../upload/1.jpg',
        showState: false
      },
      {
        id: 1,
        src: '../../upload/2.jpg',
        showState: false
      },
      {
        id: 2,
        src: '../../upload/3.jpg',
        showState: false
      },
      {
        id: 3,
        src: '../../upload/4.jpg',
        showState: false
      },
      {
        id: 4,
        src: '../../upload/5.jpg',
        showState: false
      },
      {
        id: 5,
        src: '../../upload/6.jpg',
        showState: false
      },
      {
        id: 6,
        src: '../../upload/7.jpg',
        showState: false
      },
      {
        id: 7,
        src: '../../upload/8.jpg',
        showState: false
      },
      {
        id: 8,
        src: '../../upload/9.jpg',
        showState: false
      },
      {
        id: 9,
        src: '../../upload/10.jpg',
        showState: false
      }
    ]
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    let data = this.data.imgs;
    let that = this;
    data.forEach((item, index) => {
      const viewPort = wx.createIntersectionObserver().relativeToViewport();
      viewPort.observe(`.img-${index}`,res=>{
        if (res.intersectionRatio > 0){
          let keyStr = `imgs[${index}].showState`;
          that.setData({
            [keyStr]: true
          })
        }
      })
    })
  }
})

二、骨架屏

利用微信小程序开发工具自动生成骨架屏代码的能力来实现功能。

1)如上图,点击模拟器面板右下角三点处;

2)点击生成骨架屏,将有弹窗提示是否允许插入骨架屏代码。确定后将在当前页面同级目录下生成imgsList.skeleton.wxml和 imgsList.skeleton.wxss两个文件,分别为骨架屏代码的模板和样式;

3)imgsList.wxml引入骨架屏模板,并增加hideContainer判断内容的隐藏和显示;

<!-- 引入骨架屏 -->
<import src="imgsList.skeleton.wxml"/>
<template is="skeleton" wx-if="{
   
   {loading}}" data="{
   
   {hideContainer}}" />

<view class="imgs-box" data-skeleton-hide="hideContainer" hidden="{
   
   {!hideContainer}}">
  <block wx:for="{
   
   {imgs}}" wx:key="id">
    <image class="img-{
   
   {index}}" src="{
   
   {item.showState? item.src: ''}}"></image>
  </block>
</view>

4)imgsList.wxss引入骨架屏样式;

@import "./imgsList.skeleton.wxss";

.imgs-box image {
  width: 100%;
  height: 500rpx;
}

5)imgsList.js增加参数loading和hideContainer;

Page({
  /**
   * 页面的初始数据
   */
  data: {
    loading: true,
    hideContainer: false,
    imgs: [{
        id: 0,
        src: '../../upload/1.jpg',
        showState: false
      },
      {
        id: 1,
        src: '../../upload/2.jpg',
        showState: false
      },
      {
        id: 2,
        src: '../../upload/3.jpg',
        showState: false
      },
      {
        id: 3,
        src: '../../upload/4.jpg',
        showState: false
      },
      {
        id: 4,
        src: '../../upload/5.jpg',
        showState: false
      },
      {
        id: 5,
        src: '../../upload/6.jpg',
        showState: false
      },
      {
        id: 6,
        src: '../../upload/7.jpg',
        showState: false
      },
      {
        id: 7,
        src: '../../upload/8.jpg',
        showState: false
      },
      {
        id: 8,
        src: '../../upload/9.jpg',
        showState: false
      },
      {
        id: 9,
        src: '../../upload/10.jpg',
        showState: false
      }
    ]
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    let data = this.data.imgs;
    let that = this;
    data.forEach((item, index) => {
      const viewPort = wx.createIntersectionObserver().relativeToViewport();
      viewPort.observe(`.img-${index}`,res=>{
        if (res.intersectionRatio > 0){
          let keyStr = `imgs[${index}].showState`;
          that.setData({
            [keyStr]: true,
            loading: false,
            hideContainer: true
          })
        }
      })
    })
  }
})

6)生成的imgsList.skeleton.wxml添加hidden="{ {hideContainer}}",用来判断骨架屏的显示和隐藏;

<!--
使用方法:
在 pages\imgsList\imgsList.wxml 引入模板

```
<import src="imgsList.skeleton.wxml"/>
<template is="skeleton" wx-if="{
   
   {loading}}" />
```

在 pages\imgsList\imgsList.wxss 中引入样式
```
@import "./imgsList.skeleton.wxss";
```

更多详细信息可以参考文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/skeleton.html
-->
<template name="skeleton">
  <view class="sk-container" hidden="{
   
   {hideContainer}}">
    <view class="imgs-box">
      <image class="img-0 sk-image">
      </image>
      <image class="img-1 sk-image">
      </image>
      <image class="img-2 sk-image">
      </image>
    </view>
    <view class="sk-loading-spinner">
      <view class="circular">
      </view>
    </view>
  </view>
</template>

7)生成的imgsList.skeleton.wxss样式如下:

/*

在 pages\imgsList\imgsList.wxss 中引入样式
```
@import "./imgsList.skeleton.wxss";
```

更多详细信息可以参考文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/skeleton.html
*/
.sk-image {
    background: #EFEFEF !important;
  }
.sk-container {
    background-color: transparent;
  }
 
    @keyframes loading-rotate {
      100% {
        transform: rotate(360deg);
      }
    }

    .sk-loading-spinner {
      position: absolute;
      top: 50%;
      margin-top: -0.5rem;
      width: 100%;
      text-align: center;
    }

    .sk-loading-spinner .circular {
      border-radius: 50%;
      width: 1rem;
      height: 1rem;
      display: inline-block;
      box-sizing: border-box;
      border-top: solid 2px #409eff;
      border-right: solid 2px #409eff;
      border-bottom: solid 2px #409eff;
      border-left: solid 2px transparent;
      animation: loading-rotate 1s linear infinite;
    }

猜你喜欢

转载自blog.csdn.net/king0964/article/details/108007225