仿一下B站的页面动画加载骨架

415 阅读1分钟

b站首页视频在后端接口返回来数据之前会有时间间隔,为了提高页面的加载等待这段时间的用户感知体验,在数据赋值上去之前会有动画加载骨架。说白了,就是在数据回来之前先用一些好看的动画顶着,占着那片地,很多其他官网也是如此。

效果

  • 具体效果如下:

  • 没有数据,单独看效果时

loading.gif

  • 加上请求数据,看整体效果时

loading-example.gif

原理

原理其实就是,在主长方形里面,用一个同样大小或者比这个主框大的元素背景色是线性渐变,用动画的形式从左到右移动。

代码

多说无益,上代码

架构

<div>
  // 背景图的框
  <div class="pic-card">
    <picture>
      <img src="" alt="" />
    </picture>
  </div>

  // 标题框
  <div class="pic-description">
    <div class="title"></div>
  </div>
    
  // 昵称框
  <div class="pic-description">
    <div class="title content"></div>
  </div>
</div>

css

<style>
  body {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
  }
  .pic-card, .pic-description {
    position: relative;
    width: 280px;
    height: 150px;
    border-radius: 6px;
  }
  picture, .title {
    background-color: #F1F2F3;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1;
    overflow: hidden;
    width: 100%;
    height: 100%;
    object-fit: contain;
    border-radius: inherit;
  }
  picture:before, .title::before {
    animation: image_loading 1s cubic-bezier(0.4, 0, 0.2, 1) infinite; // 动画
    background: linear-gradient(-45deg, #F1F2F3 25%, #FFFFFF 45%, #F1F2F3 65%); // 线性渐变背景 
    bottom: -20%;
    content: "";
    display: block;
    height: 140%;
    left: 0;
    position: absolute;
    top: -20%;
    width: 100%;
  }
  .pic-description {
    margin-top: 10px;
    width: 260px;
    height: 20px;
  }
  .content {
    width: 200px;
  }
  
  
  // 动画 从左到右
  @keyframes image_loading {
    0% {
      -webkit-transform: translateX(-100%);
      transform: translateX(-100%);
    }
    to {
      -webkit-transform: translateX(100%);
      transform: translateX(100%);
    }
  }
</style>

vue3 + 数据

加上数据这一步,就看数据出来之后,1、把背景色灰色搞掉;2、把移动的那个线性渐变的框搞掉;这两点就完事了。

<template>
  <div class="main">
    <div class="pic-card">
      <picture class="skeleton">
        <div v-if="data.loading" class="cover"></div>
        <img v-if="!data.loading && data.url" :src="data.url" alt="" />
      </picture>
    </div>

    <div class="description">
      <div class="skeleton title" :class="{ white: !data.loading }">
        <div v-if="data.loading" class="cover"></div>
        <div v-else>求求你给我一个认识你的机会呜呜</div>
      </div>
    </div>

    <div class="description">
      <div class="skeleton content" :class="{ white: !data.loading }">
        <div v-if="data.loading" class="cover"></div>
        <div v-else>紫菜蛋花兔兔</div>
      </div>
    </div>
  </div>
</template>
<script setup>
import { onMounted, reactive } from "vue";

const data = reactive({
  loading: true,
  url: "",
});

const fakeApi = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, 5000);
  });
};

const getList = async () => {
  data.loading = true;
  await fakeApi();
  data.loading = false;
  data.url =
    "https://i0.hdslb.com/bfs/live/user_cover/bf8980244ea0230d5a7679445ad1c75c36e31afd.jpg@672w_378h_1c_!web-home-common-cover.avif";
};

onMounted(() => {
  getList();
});
</script>

quora 图片加载动画

<div class="father">
      <div class="fyplfD" style="box-sizing: border-box; aspect-ratio: 16 / 9; margin-left: -16px; margin-right: -16px;"></div>
</div>

// css 代码
    
@keyframes image_loading {
    0% {
      -webkit-transform: translateX(-100%);
      transform: translateX(-100%);
    }
    to {
      -webkit-transform: translateX(100%);
      transform: translateX(100%);
    }
}

.father {
    width: 500px;
    height: 250px;
    background-color: rgb(241, 242, 242);
    overflow: hidden;
}

.fyplfD {
    animation: 1.2s linear 0s infinite normal forwards running image_loading;
    background-size: 200% 100%;
    background-repeat: repeat;
    background-color: rgb(241, 242, 242);
    background-image: linear-gradient(100deg, rgb(241, 242, 242) 5%, rgb(228, 230, 230) 20%, rgb(241, 242, 242) 35%);
}

效果

Kapture 2022-11-30 at 18.11.05.gif

代码链接地址