小程序之骨架屏

525 阅读2分钟

骨架屏

在小程序中,页面层与逻辑层通信,还有服务器端通信需要一定的时间,这为用户的体验带来不小的挑战。

为了减少用户等待的焦虑,以及减少用户离开页面的时间。所以让用户提前感知此页面在运行,和用户提前聚焦内容,于是使用骨架屏作为初次渲染。


绘制骨架

骨架元素分为两种:

  • 圆形
  • 长方形

长方形的绘制:

<div class="rect"></div>
<style>
  .rect {
    height: 20rpx;
    width: 100vw;
    background-color: #dcdde1;
  }
</style>

圆形只需要在正方形的基础上加上border-radius:100%即可,例子如下:

<div class="cir"></div>
<style>
  .cir {
    height: 50rpx;
    width: 50rpx;
    background-color: #dcdde1;
    border-radius: 100%;
  }
</style>

绘制的骨架

在小程序中,提供selectAll这个API可以帮我选择出页面中的元素。

选择整个页面中的.rect元素,我们将其包装成异步的模式:

async function selectAll(selector) {
  return new Promise((resolve, reject) => {
    uni
      .createSelectorQuery()
      .selectAll(selector)
      .boundingClientRect()
      .exec(res => resolve(res));
  });
}

然后就可以使用selectAll函数选择页面中的元素。

定位元素

页面中的元素定位有多种,但是骨架屏因为它不会运动,不会左右摇晃,更不会变色。

所以在页面中使用绝对定位,可以精确找到落在页面上的元素。

<div class="skeleton-contain">
  <div class="rect"></div>
</div>
<style>
  .skeleton-contain {
    height: 100vw;
    width: 100vw;
    position: absolute;
  }
  .rect {
    top: 20rpx;
    height: 20rpx;
    width: 100vw;
    background-color: #dcdde1;
  }
</style>

由于前面的selectAll函数,那么它返回的字段形如:

{
  "bottom": 262,
  "dataset": Object,
  "height": 60,
  "id": "",
  "left": 37.5,
  "right": 97.5,
  "top": 202,
  "width": 60
}

这时,我们就可以找到它的定位点在哪里,并以循环节点的方式绘制:

<template>
  <view class="skeleton-contain">
    <view
      v-for="(item, index) in rectList"
      :key="index"
      class="rect"
      :style="[
        {
          height: `${item.height}px`,
          width: `${item.width}px`
        },
        {
          top: `${item.top}px`,
          bottom: `${item.bottom}px`,
          right: `${item.right}px`,
          left: `${item.left}px`
        }
      ]"
    ></view>
  </view>
</template>
<script>
export default {
  name: 'skeleton',
  mounted() {
    this.drawRect();
  },
  data() {
    return {
      rectList: []
    };
  },
  methods: {
    async selectAll(selector) {
      return new Promise((resolve, reject) => {
        uni
          .createSelectorQuery()
          .selectAll(selector)
          .boundingClientRect()
          .exec(res => resolve(res));
      });
    },
    async drawRect() {
      const [rect] = await this.selectAll('.rect');
      this.rectList = rect;
    }
  }
};
</script>
<style>
.skeleton-contain {
  height: 100vw;
  width: 100vw;
  position: absolute;
}
.rect {
  background-color: #dcdde1;
}
</style>

这样,一个完整的骨架也就做出来了。