面试官:说一下这个Loading动画实现思路 (CSS3 实现 Loading 动画)

1,279 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

写在前面

随着研发进度的推进,自研框架 V1.0 也将开始投入使用,而最后一步优化 Loading 动画是最轻松,也是最好玩的事情了,毕竟每个前端都有一个自己做 UI 设计的梦。

需求分析

那么设计个什么 Loading 动画才能显得既大气、优雅,又富有科技感呢,一共有几个方案

1、Gif 图

使用酷炫的 gif 图作为主图,加上必要的背景衬托,构造出一个炫酷的动画效果。这是第一个被 PASS 的方案,首先加载图需要时间,会影响首屏渲染,无法充分发挥 Loading 的使命。

4e117c7156d18fafb6a1d945c9bd307b.gif

2、使用 SVG 绘制粒子群动画

看起来还是挺酷炫的,但是有个问题是多少有点花里胡哨的感觉了,毕竟我们不只是为了酷炫,

25b6436ce8e4aa816bb756a2689ff35a.gif

3、使用 CSS3 构造 Loading 动画

相对于前两种,这种实现方式虽然没有 SVG 那么酷炫,但是兼顾简洁、大气,感觉上就比较符合公司的气质,相对于第一种,性能上也做到了更小的消耗,所以最终选择了第三种方案,接下来就简单看看实现方式 GIF 2022-6-1 22-23-02.gif

方案实现

定义元素结构

<template>
  <div class="Loading-container">
    <div class="inner-box">
      <div class="inner"></div>
      <div class="inner"></div>
      <div class="inner"></div>
      <div class="inner"></div>
      <div class="inner"></div>
      <div class="inner"></div>
    </div>
  </div>
</template>

正方体由六个面组成,由六个 div 通过选择和偏移,组成一个立体的正方体

样式

外容器样式

transform-style: preserve-3d;transform-origin: 0 0 ==> 用于启用 CSS3 3D 样式 transform: rotateX(-30deg) rotateY(45deg) translate(0,0) ==> 用于改变视角与立方体的相对位置,使立体效果能够呈现出来

.inner-box {
  position: relative;
  width: 72px;
  height: 72px;
  transform-style: preserve-3d;
  transform-origin: 0 0;
  transform: rotateX(-30deg) rotateY(45deg) translate(0,0);
}

立方体六个面样式

这里主要设置面的颜色、边框等基础样式,并没有什么特殊的设置

.inner {
  position: absolute;
  width: 72px;
  height: 72px;
  text-align: center;
  line-height: 72px;
  color: #fff;
  border-radius: 6px;
  background: rgba(255,255,255,.3);
  border: 2px solid rgba(255,255,255,.3);
}

六个面的偏移与旋转

最关键的就是这里的旋转与偏移了,如图,这里以中间绿色的容器为基础面,并标出了六个面的名称,可以看出来,“前面”以及“后面”是通过相当于容器位置前后偏移得到,样式如下

:nth-child(5) {
  transform: translateZ(36px);
  animation: 3s front infinite;
}
:nth-child(6) {
  // 旋转180deg是为了使字在外部看起来正常,不影响面的位置
  transform: rotateY(180deg) translateZ(36px);
  animation: 3s back infinite;
}

image.png

而“上面”和“下面”则是通过旋转+平移实现,以“上面”为例,首先选择 90deg 至于容器垂直,然后平移至边缘即可,样式如下

:nth-child(1) {
  transform: rotateX(90deg) translateZ(36px);
  animation: 3s top infinite;
}
:nth-child(2) {
  transform: rotateX(-90deg) translateZ(36px);
  animation: 3s bottom infinite;
}

image.png

最后剩下的“左面”和“右面”与“上下面”同理,通过选择+平移实现,样式如下

:nth-child(3) {
  transform: rotateY(90deg) translateZ(36px);
  animation: 3s left infinite;
}
:nth-child(4) {
  transform: rotateY(-90deg) translateZ(36px);
  animation: 3s right infinite;
}

image.png

最后完成的正方体如图

image.png

动画

动画一共分为两部分,容器整体动画与面的单独动画

容器动画

GIF 2022-6-1 23-51-08.gif

容器的动画主要是循环旋转,每次选择 360 deg,动画如下

.inner-box {
    animation: 3s ctn  infinite;
}
@keyframes ctn {
  from {
    transform: rotateX(-35deg) rotateY(45deg) translate(-50%,-50%);
  }
  50% {
    transform: rotateX(-35deg) rotateY(405deg) translate(-50%,-50%);
  }
  to {
    transform: rotateX(-35deg) rotateY(405deg) translate(-50%,-50%);
  }
}

六个面动画

GIF 2022-6-1 23-57-02.gif

六个面的动画主要是平移,为了展示拆开的效果,所以六个面的动画时分别向远离中心的方向平移,如下,以“上面”为例

// 其他六个面同理,省略
:nth-child(1) {
  animation: 3s top infinite;
}

@keyframes top {
  from {
    transform: rotateX(90deg) translateZ(36px);
  }
  50% {
    transform: rotateX(90deg) translateZ(36px);
  }
  75% {
    transform: rotateX(90deg) translateZ(72px);
  }
  to {
    transform: rotateX(90deg) translateZ(36px);
  }
}
@keyframes bottom {
  from {
    transform: rotateX(-90deg) translateZ(36px);
  }
  50% {
    transform: rotateX(-90deg) translateZ(36px);
  }
  75% {
    transform: rotateX(-90deg) translateZ(72px);
  }
  to {
    transform: rotateX(-90deg) translateZ(36px);
  }
}
@keyframes left {
  from {
    transform: rotateY(90deg) translateZ(36px);
  }
  50% {
    transform: rotateY(90deg) translateZ(36px);
  }
  75% {
    transform: rotateY(90deg) translateZ(72px);
  }
  to {
    transform: rotateY(90deg) translateZ(36px);
  }
}
@keyframes right {
  from {
    transform: rotateY(-90deg) translateZ(36px);
  }
  50% {
    transform: rotateY(-90deg) translateZ(36px);
  }
  75% {
    transform: rotateY(-90deg) translateZ(72px);
  }
  to {
    transform: rotateY(-90deg) translateZ(36px);
  }
}
@keyframes front {
  from {
    transform: translateZ(36px);
  }
  50% {
    transform: translateZ(36px);
  }
  75% {
    transform: translateZ(72px);
  }
  to {
    transform: translateZ(36px);
  }
}
@keyframes back {
  from {
    transform: rotateY(180deg) translateZ(36px);
  }
  50% {
    transform: rotateY(180deg) translateZ(36px);
  }
  75% {
    transform: rotateY(180deg) translateZ(72px);
  }
  to {
    transform: rotateY(180deg) translateZ(36px);
  }
}

总结

对于 CSS3 基本上没啥实现难度,主要就是动画、位置过渡效果细节的把握,也算是自己体验了一次做设计师的乐趣吧,虽然简单,但是对于动画的微调还是感受到和 UI 设计师之间的巨大差距,希望在前端之余也能够向 UI 设计师们学习更多酷炫的技能吧!

往期好文推荐

面试官:说说从输入 URL 到页面显示到底经历了什么,体现一下你的知识广度

面试官:作为前端,服务器相关了解多少?

面试官:HTTPS 采用的是对称加密还是非对称加密?具体说说其加密过程

面试官:说说 Cookie 和 Token 的区别?

面试官:网络安全了解多少,简单说说?(一)

面试官:网络安全了解多少,简单说说?(二)

面试官:网络安全了解多少,简单说说?(三)

面试官:网络安全了解多少,简单说说?(四)

面试官:网络安全了解多少,简单说说?(五)

面试官:网络安全了解多少,简单说说?(六)

面试官:网络安全了解多少,简单说说?(七)

面试官:网络安全了解多少,简单说说?(八)

浅尝 | 从 0 到 1 Vue 组件库封装

面试官:这么简单的正则表达式都不会?

Webpack 打包类库踩坑

面试官:你就只会 npm run build 吗?(Webpack 配置 Vue+Ts)

面试官:连VuePress都没搭过还说开发过组件库?(VuePress 搭建)

面试官: 连 Vue 视图更新都不会写?(Vue视图更新原理【一】)

面试官: 能不能手写 Vue 响应式?(Vue2 响应式原理【完整版】)

面试官:能不能手写 Vue3 响应式(Vue3 原理解析之响应系统的实现)

JS 优雅之道(JS 代码优化小 Tip)

面试官:你真的会用 SVG 吗? (SVG 应用实战)

面试官:说一下这个Loading动画实现思路 (CSS3 实现 Loading 动画)

JS 扫盲题 ( 面试题梳理系列 (一))

面试官:你确定你说的防抖不是节流吗?( 面试题梳理系列 (二))

面试官:除了 HTTP,你还用过什么通信协议?(Websocket 在数字孪生中的应用)

面试官:你真的理解 Event Loop 吗?( JS 事件循环 )

面试官:v-for 中 key 为什么不能用 index,从原理层面聊聊?

面试官:vue-router 的 hash 与 history 哪个模式会刷新页面?

面试官:说说你平时用过的自适应方案(数字孪生可视化自适应方案)

面试官:说一下如何优化过渡动画(数字孪生可视化过渡动画)

写在最后

博主接下来将持续更新好文,欢迎关注博主哟!!
如果文章对您有帮助麻烦亲点赞、收藏 + 关注和博主一起成长哟!!❤️❤️❤️