【动画而已】手摸手带你实现 WINDOWS 加载动画

画图画腻了,大伙也不太爱看。这个估计也不爱看 随缘。今天换个口味,耍耍动画,就以 windows 的加载为例,手摸手一步一步来实现它。

0x0 效果预览

windows_003

0x1 分析动画

  1. 有多个小球围绕中心作圆周运动
  2. 每个小球的运动方式相同,只是有不同延迟
  3. 好像也没啥了,简简单单

0x2 基本结构

既然是相同的动画,那么我们就从最基础的开始:先实现一个小球的运动。

写出如下HTML结构

<main>
  <i></i>
</main>

main 为包裹层,你用 div也是一样,i标签为小球。

给出基本样式

CSS CODE
* {
  margin:0;
  padding:0;
}

html,
body {
  height: 100vh;
  background: #0077d5;
  display: grid;
  place-items: center;
}

main {
  position: relative;
  width: 20px;
  height: 20px;
  border: 1px dashed #fff;
  border-radius: 50%;

  i {
    position: absolute;
    inset: 0;
    background: #fff;
    border-radius: 50%;
  }
}

上述代码,只是给定背景色、小球变为圆形,并且给中心点增加了个虚线效果(方便我们观察)

之所以看到是这个样子,是因为目前小球的位置是绝对定位了。
其中 inset: 0 是 top:0; left:0; right:0; bottom:0 的简写形式。
在此处含义就是让小球 i 与 容器 main 的大小相同。

0x3 动画制作

基本的结构已经搭建,下一步我们该着手动画了。
在写动画之前,我们仍需要思考一下该动画的运作方式:

  1. 我们当前小球与容器合体了,如何使用使小球位移到外围呢?
  2. 完成第一步后,如何让小球能够绕中心点作圆周运动呢?

从第一个问题着手:众所周知,css 中 的 transform 属性中的 translate 可以使元素位移。那我们就试一下

i {
 // ...
 transform: translate(100px,0);
}

此时小球果然位移到外围了,坐标是 x:100px y:0

该进行第二步了。那么怎样才能绕中心点运动呢?这也是本动画的难点所在。
还是和之前一样,既然整体暂时没有思路,那么我们就再拆开:拿一个位置点来思考。

圆的其中一个特性就是,圆周上任何一点到圆点的距离相等。

假设我们圆的半径为1,是从 水平位置 逆时针运动,那么小球每次与初始位置的夹角 为 θ,我们根据小学二年级学的三角函数可知 ,它的纵向坐标是不是可以直接表达成 sinθ ;横向坐标那就顺理成章表达为 cosθ

\sin\theta = \cfrac{y}{1}      \cos\theta = \cfrac{x}{1}

小球的实时位置坐标搞定了,接下来就是考虑动画了。

css动画,那必须少不了主角 animation ,搭配 transformtranslate 属性,我们就可以让小球动起来

// ...
main {
  --angle: 0;

i {
  translate: calc(sin(var(--angle)) * 100px) calc(cos(var(--angle)) * 100px);
  animation: rotate 3s infinite linear;
  }
}

上述代码中,我们使用到了 calc计算函数,它可以根据数值自动来计算最终结果,这个非常有用,这里我们乘了一个系数 100,意思是将半径扩大到100px。另外,我们还定义了一个变量 angle,初始值为0deg,配合关键帧 keyframes 让它来实时改变。

@keyframes rotate {
  0% {
    --angle: 0deg;
  }
  100% {
    --angle: 360deg;
  }
}

想象很美好,然而不幸的是,小球并没有要动的意思,问题出在哪里呢?
在之前的几期中,我们已经熟练使用自定义属性(变量)来定义颜色,然而它并不能直接支持动画,因为CSS动画和过渡需要对属性的具体值进行插值。可以简单理解为 它是静态的,不能够动态改变。

为了解决这个问题,我们引出另一个黑科技 CSS Houdini API,其中有一个就是我们今天要用到的 @property。它的基本语法如下:

@property --property-name {
  syntax: '<color>';  // 属性类型
  inherits: false;  // 是否允许继承
  initial-value: #fff;  // 默认值
}

关于它的详细描述,请参阅 MDN - @property

:light_bulb:CSS Houdini API

CSS Houdini 是一组低级API,它们允许开发者通过JavaScript直接与浏览器的CSS引擎进行交互,从而创建和控制自定义的CSS功能。Houdini 的目标是将一些浏览器内部的渲染流程开放给开发者,使得他们可以更灵活、更高效地创建复杂的视觉效果和动画,而不必等待浏览器原生支持这些特性。

OK,到此为止,知识点基本讲解完了,那么就直接使用起来,先让小球运动起来吧!

@property --angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

此时单个小球已经完美运动,将 linear 改为 ease-in-out 拥有更好的视觉效果

windows_001

0x4 继续完善

好啦,单个小球的运动已经搞定,接下来就是继续将动画做完整,知识点都说完了,直接贴代码:

HTML
<main>
  <i></i>
  <i></i>
  <i></i>
  <i></i>
  <i></i>
  <i></i>
  <i></i>
  <i></i>
</main>

我们将小球数量增加到了8个,但是好像预览结果与之前单个小球一样!产生这种原因是因为小球的位置在相同的地方,重合了。解决方法也很简单,给每一个小球增加一个动画延迟 animation-delay。写到这里有点累了,直接用sass弄一个:

i {
// ...
@for $i from 1 to 8 {
    i:nth-child(#{$i}) {
      animation-delay: #{$i * -0.12}s;
    }
  }
}

上述代码就是遍历给每个 li 添加不同的延迟,这里使用到负值,是为了让小球提前运动,避免等待。可以自己改成正值,对比一目了然,就不展开细说。延迟的基础系数可以自己调整到适合自己的,看效果:

windows_002

0x5 再酷一点

到此位置,可以说是已经完成,但是,还能再酷一点。

我们对之前的sass稍作修改,让每个小球拥有不同的背景

 @for $i from 1 to 9 {
    i:nth-child(#{$i}) {
      animation-delay: #{$i * -0.12}s;
      background-color: #{hsl($i * 20 + 10, 80%, 60%, 85%)};
    }
  }

hsl() 函数标记根据其色相饱和度明度 来表达 sRGB 颜色。
可选的 alpha 成分代表了颜色的透明度。

——摘自MDN

再看眼最终效果

windows_003

0x6 完整代码

点赞开启
HTML
<main>
  <i></i>
  <i></i>
  <i></i>
  <i></i>
  <i></i>
  <i></i>
  <i></i>
  <i></i>
</main>
SASS
@property --angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

html,
body {
  height: 100vh;
  background: #0077d5;
  display: grid;
  place-items: center;
}

main {
  position: relative;
  width: 20px;
  height: 20px;
  border: 1px dashed #fff;
  border-radius: 50%;

  i {
    position: absolute;
    inset: 0;
    background: #fff;
    border-radius: 50%;
    translate: calc(sin(var(--angle)) * 100px) calc(cos(var(--angle)) * 100px);
    animation: rotate 3s infinite ease-in-out;
  }

  @for $i from 1 to 9 {
    i:nth-child(#{$i}) {
      animation-delay: #{$i * -0.12}s;
      background-color: #{hsla($i * 20 + 10, 80%, 60%, 85%)};
    }
  }
}

@keyframes rotate {
  0% {
    --angle: 0deg;
  }

  95%,
  100% {
    --angle: 360deg;
  }
}

本期结束,能看完的都是勇士!



图穷匕见,求赞,求认可 :blush:

33 个赞

太牛了 :tieba_013:

3 个赞

太强了 大鹅

3 个赞

哇塞 真就实现了这个

鹅你也太强了吧

2 个赞

不是,你们真的都是css手搓动画吗?

2 个赞

写晕了,忘了贴代码,稍等

3 个赞

哇 还是你的动画好

1 个赞

我给鹅这个CSS动画打 :100:

2 个赞

那我大99分 怕多1分他骄傲

2 个赞

真鹅不说假话

3 个赞

赞都不舍得点的屑龙龙

2 个赞

谢龙龙!我自罚三鞋板 1f915 1f915 1f915

3 个赞

大佬好强~又学到了

2 个赞

共同进步 tieba13

3 个赞

我来了,大鹅还是那么牛逼

2 个赞

猫老师又开始吹了,要飘了

3 个赞

哈哈,我说的明明是事实啊

2 个赞

这种东西还是没意思,骗不到佬友们的赞:+1:t2:。不如水帖去,开摆 :grin:

2 个赞

大鹅老师赞

2 个赞

既然你都这么说了,那就开摆吧 :tieba_025:

2 个赞