夏日里,分享一个清新凉爽的纯CSS3电风扇的动画DEMO效果

4,535 阅读20分钟

我正在参与掘金创作者训练营第5期,点击了解活动详情

前言

最近天气温度居高不下,在曾经的四大火盆之一的城市居住,再安逸闲适的城市也让人干燥烦热。就在昨天,新开了一个专栏,主要是想做一个关于CSS特殊效果的专栏,想能给大家带来一丝惬意,继而忘掉燥热的酷暑天气。其实最真实的目的是为了将自己遇见的,好玩有趣的特效收集整理,然后通过掘金这个大平台展现给大家,也让大家来看看这些牛皮扎劲的CSS特效效果到底是怎么实现的,从而启发自己在以后的工作中的思维想法,最后也能写出牛皮酷炫的特效示例。

专栏中会有自己实现的特效,也会有别人实现的特效,前期肯定还是以分享别人的为主。主要原因还是因为平时的工作是和业务代码打交道居多,至于样式效果仅仅只是还原UI设计稿而已,没有太多精力时间像别人去专研那些特效效果,其次就是实力不济呀,所以这次就开个专栏,边分享的同时也试着去理解别人的实现思路,为以后自己亲自操刀打下坚实基础。

背景

前言里面提到了,正是酷暑之季,每天都是把空调开到最低才能抵抗得了这炎热。奈何家境贫寒,承受不了高昂的空调电费,真是吹起一时爽,交费泪两行呀。一直开空调也不是事儿,这不得想个办法减少成本,于是选择在网上冲浪,这应该能降降高温。正浪的飞起时,看到有一个风扇优哉游哉的转着......诶,瞬间感觉清爽了不少,我又是喜欢分享的人,这不,看文章的小伙伴们福利来了,我也把这风扇分享给你们,也让你们清凉一夏。(注意看,我还是给你们开的最大档哦)

动画.gif

(音乐起:怎么大风越狠,我心越荡啊~ 吹啊吹啊~ 我赤脚不害怕~ 吹啊吹啊~ 无所谓扰乱我)

实现

没错,这次就是来看看别人是怎么实现这个风扇特效的,我要好好看看,到底是好大个风扇我搞不定!

一、组织框架和定义变量

万事开头难,没有开头何来过程,何来Ending呢?所以做一件事还是先有个大体的规划,架子搭好,后续往里面不断地填内容就可以了。一个马拉松你可能一鼓作气跑不下来,但是如果把整段路程拆分为多个几个公里,一个几公里一个几公里的去跑完,最后一个马拉松不就跑下来了么,编码依然如此。

1.1 总体框架

总的架子没啥难度,就是使用H5的最新快捷键,一按就生成了。接着就是使用normalize重置默认样式,引入项目样式文件style.css即可。

<!DOCTYPE html>
<html lang="en" >
<head>
  <meta charset="UTF-8">
  <title>纯CSS3电风扇动画DEMO演示</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
<div class="scene">
  <div class="fan"></div>
</div>
</body>
</html>

1.2 定义变量

平时开发中我们也是把项目中公用的模块定义好,然后其他模块引入使用的思路,这里同样也是把公用的样式提取出来,定义好即可。

:root {
  --blade-speed: 1; /*叶片速度*/
  --rotation: 25; /*旋转角度*/
  --fan-speed: 2; /*风速*/
  --state: running; /*状态*/
  --bg: #344c65; /*背景色*/
  --shade-one: #f2f2f2;
  --shade-two: #e6e6e6;
  --shade-three: #d9d9d9;
  --shade-four: #ccc;
  --shade-five: #bfbfbf;
  --shade-six: #b3b3b3;
  --shade-seven: #a6a6a6;
  --shade-eight: #999;
  --cage-one: rgba(255,255,255,0.4);
  --cage-two: rgba(255,255,255,0.2);
  --height: 70vmin;
  --width: 40vmin;
}

说实话,这真是小刀剌屁股,开了眼了。恕我才疏学浅,我第一次知道原生CSS中还可以这样定义变量,工作中都是使用的lesssass去做的,没想到原生CSS中是这样定义变量在全局中使用的,牛👍,涨姿势了!

然后把架子搭建时的容器样式提前写好,后面也是来做样式的填空题就好啦。如下:

.scene {
  position: absolute;
  height: var(--width);
  width: var(--width);
  top: 50%;
  left: 50%;
  transform: translate3d(-50%, -50%, 0vmin) rotateX(-24deg) rotateY(34deg) rotateX(90deg);
}
.fan {
  height: var(--height);
  width: var(--width);
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) rotateX(-90deg) rotateX(calc(var(--rotateX, 0) * 1deg)) rotateY(calc(var(--rotateY, 0) * 1deg));
}

这样,这个风扇实现的第一步就搞定了,接下来就是一个个挨着填空就可以完成整个特效了。上述代码实现的效果就是一个背景墙而已,如下:

image.png

二、底座

定义变量给我挫败感,但是我得坚强的往下走🔜

首先还是来看看底座是如何实现的吧。如果要我来实现这个底座,我觉得我是可以搞出来的,就是代码可能会写得有点多,那还是来看看别人是怎么个实现过程,好好看好好学,眼睛不要打眺,打眺就望不到。

<div class="fan__base">
  <div class="cuboid base">
    <div class="cuboid__side"></div>
    <div class="cuboid__side"></div>
    <div class="cuboid__side"></div>
    <div class="cuboid__side"></div>
    <div class="cuboid__side"></div>
    <div class="cuboid__side"></div>
  </div>
</div>

嗯~这是要用6个盒子组成一个长方体底座的节奏呀,那到底该怎么放置它才能组成一个长方体呢?往下瞧👇

.fan__base {
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translate(-50%, 0);
  height: 8%;
  width: 80%;
}
.base {
  --thickness: calc(40 * 0.8);
}
.base div {
  background: var(--shade-two);
}
.base div:nth-of-type(1) {
  background: var(--shade-one);
}
.base div:nth-of-type(5) {
  background: var(--shade-three);
}
.base div:nth-of-type(4) {
  background: va(--shade-six);
}
.cuboid {
  width: 100%;
  height: 100%;
  position: relative;
}
.cuboid__side:nth-of-type(1) {
  height: calc(var(--thickness) * 1vmin);
  width: 100%;
  position: absolute;
  top: 0;
  transform: translate(0, -50%) rotateX(90deg);
}
.cuboid__side:nth-of-type(2) {
  height: 100%;
  width: calc(var(--thickness) * 1vmin);
  position: absolute;
  top: 50%;
  right: 0;
  transform: translate(50%, -50%) rotateY(90deg);
}
.cuboid__side:nth-of-type(3) {
  width: 100%;
  height: calc(var(--thickness) * 1vmin);
  position: absolute;
  bottom: 0;
  transform: translate(0%, 50%) rotateX(90deg);
}
.cuboid__side:nth-of-type(4) {
  height: 100%;
  width: calc(var(--thickness) * 1vmin);
  position: absolute;
  left: 0;
  top: 50%;
  transform: translate(-50%, -50%) rotateY(90deg);
}
.cuboid__side:nth-of-type(5) {
  height: 100%;
  width: 100%;
  transform: translate3d(0, 0, calc(var(--thickness) * 0.5vmin));
  position: absolute;
  top: 0;
  left: 0;
}
.cuboid__side:nth-of-type(6) {
  height: 100%;
  width: 100%;
  transform: translate3d(0, 0, calc(var(--thickness) * -0.5vmin)) rotateY(180deg);
  position: absolute;
  top: 0;
  left: 0;
}

看到没有,在base这个容器下定义一个变量--thickness,下面所有的元素都能使用,而base外的其他元素却使用不了,要想使用就只能使用root中定义的,可能这就是原生CSS中的全局变量和局部变量吧。

下面就来看看上述代码最终实现的效果,如下:

image.png

三、控制器

接着来实现开关和档位器,其实思路也是和底座相同,也是用6个div来组成,唯一的不同的就是形状而已。要注意的点只是位置上的排列要使其看起来合理就行,其他也就没啥难度,至于控制器的交互放在后面来聊,先来把UI实现了。

<div class="fan__controls">
  <label class="fan__control" for="zero">
    <div class="cuboid control">
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
    </div>
  </label>
  <label class="fan__control" for="one">
    <div class="cuboid control">
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
    </div>
  </label>
  <label class="fan__control" for="two">
    <div class="cuboid control">
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
    </div>
  </label>
  <label class="fan__control" for="three">
    <div class="cuboid control">
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
      <div class="cuboid__side"></div>
    </div>
  </label>
</div>

看上面的代码,我又学到一个技术点,那就是label标签中的for属性有什么用?如果我没看这个示例,我一时半会不知道,被问及时多半会是懵逼的。label标签中的for属性其实就是将label和表单控件绑定在一起

怎么理解?比如有个输入框,如果想要选中输入框获得焦点,只有input情况下,只能选中输入框;但加了label就把label和input绑定在一起后,点击label也可以选中输入框。所以后面控制器的控制功能就是通过for将其和input联系起来,通过使用input的原生属性实现交互功能,而不是通过js逻辑去操作DOM实现的,看到最后你就知道了。

然后,就是来写样式代码,如下:

.fan__controls {
  height: 6%;
  width: 50%;
  position: absolute;
  bottom: 6%;
  left: 50%;
  transform: translate3d(-50%, 0, calc(var(--width) * 0.3));
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-gap: 6%;
}
.control {
  --thickness: calc(((40 * 0.5) - ((40 * 0.5) * 0.18)) / 3);
}
.control div {
  background: var(--shade-five);
}
.control div:nth-of-type(1) {
  background: var(--shade-three);
}
.control div:nth-of-type(5) {
  background: var(--shade-six);
}
.control div:nth-of-type(4) {
  background: va(--shade-eight);
}
.fan__control:nth-of-type(1) .control {
  --shade-three: #f7d4ba;
  --shade-five: #f2b78c;
  --shade-six: #f0a875;
  --shade-eight: #eb8b47;
}

从上面代码可以看到,类名为cuboid__side的盒子并没有写样式,因为在底座的时候已经贴出了,它们两者是共用的,只需要改变宽高即可。这里还需要注意一个点,那就是可以看到选择div容器时使用的是nth-of-type而不是nth-child,这是因为:nth-child()选择器是选取属于其父元素的不限类型的第n个子元素的所有元素。而:nth-of-type()选择器选取的是属于其父元素的特定类型的第n个子元素的所有元素

意思就是:nth-child()不限制类型,:nth-of-type()限制类型。举个例:

<div id="wrap">
    <p>p1</p>
    <span>span1</span>
    <p>p2</p>
    <p>p3</p>
    <p>p4</p>
</div>
<style>
#wrap p:nth-of-type(3) {
    background: red;
}

#wrap p:nth-child(3) {
    background: yellow;
}
</style>

直接上效果,你就知道两者的区别了,如下:

image.png

:nth-of-type()会去找<p>标签进行匹配,而:nth-child()只会对其child不限类型的进行匹配。

回过头来,看上面控制器代码实现的效果。如下:

image.png

四、立柱

通过上面的代码实现和效果图,可以看到我们离完成这个风扇只差立柱和扇头了,这个电风扇已经有点雏形了。那趁热打铁,来看看立柱是怎么实现的,有没有使用啥我们不知道的技术点,请往下瞧👇

<div class="fan__spine">
  <div class="cuboid spine">
    <div class="cuboid__side"></div>
    <div class="cuboid__side"></div>
    <div class="cuboid__side"></div>
    <div class="cuboid__side"></div>
    <div class="cuboid__side"></div>
    <div class="cuboid__side"></div>
  </div>
</div>

可以看到,和底座、控制器一样,也是使用6div拼成的立柱。那没有啥好说的,直接看样式代码。如下:

.fan__spine {
  height: 57.5%;
  bottom: 8%;
  width: 20%;
  position: absolute;
  left: 50%;
  transform: translate3d(-50%, 0%, calc(var(--width) * -0.25));
}
.spine {
  --thickness: calc(40 * 0.2);
}
.spine div {
  background: var(--shade-three);
}
.spine div:nth-of-type(1) {
  background: var(--shade-two);
}
.spine div:nth-of-type(5) {
  background: var(--shade-four);
}
.spine div:nth-of-type(4) {
  background: va(--shade-seven);
}

平平无奇,少了一开始的惊喜✈,那直接看实现效果,如下:

image.png

五、旋转轴部分

略过了立柱的平淡,从这里开始到最后都是重头戏,要注意啦!主角往往都是最后登场的,开整!

旋转轴部分其实就是控制扇头摇摆的部分,一般的电风扇这一部分包括2个主件:转子和控制摇头的按钮,下面就挨着实现👇。

5.1 旋转轴

所谓旋转轴,就是电风扇立柱上装转子的那一部分,控制电风扇左右摇摆的主体,所以我们来看看是如何实现的👇

<div class="fan__head">
    <div class="fan__rotater">
        <div class="cuboid rotater">
            <div class="cuboid__side"></div>
            <div class="cuboid__side"></div>
            <div class="cuboid__side"></div>
            <div class="cuboid__side"></div>
            <div class="cuboid__side"></div>
            <div class="cuboid__side"></div>
        </div>
    </div>
</div>

抱歉,失算了,这个老六😅。还是用的之前的套路实现的,真是一招鲜吃遍天。

.fan__head {
  height: var(--width);
  width: var(--width);
  position: absolute;
  top: 0;
  left: 0;
  transform: translate3d(0, 0, calc(var(--width) * -0.25));
  -webkit-animation: fan calc(var(--fan-speed, 1) * 1s) infinite alternate ease-in-out var(--state);
          animation: fan calc(var(--fan-speed, 1) * 1s) infinite alternate ease-in-out var(--state);
}
.fan__rotater {
  top: 50%;
  width: calc(var(--width) * 0.2);
  height: calc(var(--width) * 0.2);
  position: absolute;
  left: 50%;
  transform: translate(-50%, -50%);
}
.rotater {
  --thickness: calc(40 * 0.2);
}
.rotater div {
  background: var(--shade-two);
}
.rotater div:nth-of-type(1) {
  background: var(--shade-one);
}
.rotater div:nth-of-type(5) {
  background: var(--shade-three);
}
.rotater div:nth-of-type(4) {
  background: va(--shade-six);
}

可以看到,代码中出现了animation,它就是CSS中的动画属性,也就是用它来控制电风扇转起来,摇起来的属性。它的用法如下:

image.png

至于电风扇怎么转起来的,放在后面来讲,下面来看上述代码实现后的效果,如下:

image.png

5.2 摇头杆

然后,接着来实现控制摇头的按钮。👇

<div class="fan__head">
    <div class="fan__rotater">
        <div class="cuboid rotater">...</div>
        <div class="fan__stalk">
            <div class="cuboid stalk">
                <div class="cuboid__side"></div>
                <div class="cuboid__side"></div>
                <div class="cuboid__side"></div>
                <div class="cuboid__side"></div>
                <div class="cuboid__side"></div>
                <div class="cuboid__side"></div>
            </div>
        </div>
    </div>
</div>

赢了,又是这样的👍。那也不多哔哔,样式代码如下:

.fan__stalk {
  height: 150%;
  left: 50%;
  bottom: 0;
  transform: translate(-50%, 0);
  transition: transform 0.2s cubic-bezier(0, 1.4, 0.2, 1.4);
  width: 25%;
  position: absolute;
}
.stalk {
  --thickness: calc(40 * 0.05);
}
.stalk div {
  background: var(--shade-four);
}
.stalk div:nth-of-type(1) {
  background: var(--shade-three);
}
.stalk div:nth-of-type(5) {
  background: var(--shade-five);
}
.stalk div:nth-of-type(4) {
  background: va(--shade-eight);
}

上述代码实现的效果,如下:

image.png

对,控制电风扇是否摇头的就是图中这个灰色的juju(四川话),还是挺逼真的,哈哈哈。

六、连接头

连接头就是把转头扇头连接起来的中间件。应该还是使用之前的套路,go👇

<div class="fan__head">
    <div class="fan__rotater">...</div>
    <div class="fan__barrel">
      <div class="cuboid barrel">
        <div class="cuboid__side"></div>
        <div class="cuboid__side"></div>
        <div class="cuboid__side"></div>
        <div class="cuboid__side"></div>
        <div class="cuboid__side"></div>
        <div class="cuboid__side"></div>
      </div>
    </div>
  </div>
</div>

......(😐😷👍)

.fan__barrel {
  height: 22.5%;
  width: 22.5%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate3d(-50%, -50%, calc(var(--width) * 0.3));
}
.barrel {
  --thickness: calc(40 * 0.5);
}
.barrel div {
  background: var(--shade-three);
}
.barrel div:nth-of-type(1) {
  background: var(--shade-two);
}
.barrel div:nth-of-type(5) {
  background: var(--shade-four);
}
.barrel div:nth-of-type(4) {
  background: va(--shade-seven);
}

来吧,看效果,如下:

image.png

七、扇头

看到上面实现的效果,是不是感觉希望就前方了,已经有那味了。那接下来就来实现扇头,千呼万唤始出来呀。电风扇的灵魂来了~

扇头分为了3部分:后罩、扇片和前罩。完全是根据现实中电风扇有的部件来挨着实现的,不得不说是真的周到👍。

7.1 后面罩子

罩子应该是个圆的吧,如果我没想错的话,应该没有长方体或正方体类型的电风扇吧。反正我是没看见过,那看看到底是个啥样的,弄!

<div class="fan__head">
    <div class="fan__rotater">...</div>
    <div class="fan__barrel">...</div>
    <div class="fan__housing">
        <div class="fan__housing-rear"></div>
    </div>
</div>

好的,第一个顾虑打消✅,没有看到6div了😁。那看看在样式上会不会被偷塔,如下:

.fan__housing {
  height: 150%;
  width: 150%;
  border-radius: 50%;
  top: 50%;
  left: 50%;
  position: absolute;
  border: 1vmin solid var(--shade-one);
  background: var(--cage-one);
  transform: translate3d(-50%, -50%, calc(var(--width) * 0.45));
}
.fan__housing-rear,
.fan__housing-front {
  position: absolute;
  top: 50%;
  left: 50%;
  height: 80%;
  width: 80%;
  background: var(--cage-two);
  border-radius: 50%;
  border: 1vmin solid var(--shade-one);
}
.fan__housing-rear {
  transform: translate3d(-50%, -50%, calc(var(--width) * -0.1));
  border: 1vmin solid var(--shade-two);
}

没问题,这次真的和前面的不一样了。确定的说罩子是个圆的啦,没有非主流✅。看看效果呢,如下:

image.png

哈哈哈,跟个信号接收器一样,和以前看电视需要安装的“天锅”有几分神似。有那味了~有那味了~~

7.2 叶片

接着来为上面的“天锅”补点东西,让它不是“天锅”看看。

<div class="fan__head">
    <div class="fan__rotater">...</div>
    <div class="fan__barrel">...</div>
    <div class="fan__housing">
        <div class="fan__housing-rear"></div>
        <div class="fan__blades">
          <div class="fan__blade"></div>
          <div class="fan__blade"></div>
          <div class="fan__blade"></div>
        </div>
    </div>
</div>

从代码可以看到,这个电风扇装了3个叶片,至于怎么排放,那就让样式代码来安排它。如下:

.fan__blades {
  position: absolute;
  top: 50%;
  left: 50%;
  height: 16%;
  width: 16%;
  transform: translate3d(-50%, -50%, -1px) rotate(0deg);
  -webkit-animation: rotate calc(var(--blade-speed, 0) * 1s) infinite linear;
          animation: rotate calc(var(--blade-speed, 0) * 1s) infinite linear;
}
.fan__blade {
  height: 300%;
  width: 100%;
  background: var(--shade-one);
  position: absolute;
  top: 50%;
  left: 50%;
  transform-origin: 50% 0;
  transform: translate(-50%, 0) rotate(calc(var(--rotate, 0) * 1deg));
}
.fan__blade:nth-of-type(1) {
  --rotate: 0;
}
.fan__blade:nth-of-type(2) {
  --rotate: calc(360 / 3 * 1);
}
.fan__blade:nth-of-type(3) {
  --rotate: calc(360 / 3 * 2);
}

最终,3个叶片的位置排放效果图如下所示:

image.png

7.3 前面罩子

现在离完整的电风扇模型就只差一个罩子了,有了后罩的实现,前罩也应该没啥问题。如下:

<div class="fan__head">
    <div class="fan__rotater">...</div>
    <div class="fan__barrel">...</div>
    <div class="fan__housing">
        <div class="fan__housing-rear"></div>
        <div class="fan__blades">...</div>
        <div class="fan__housing-front">
            <img src="https://assets.codepen.io/605876/avatar.png"/>
        </div>
    </div>
</div>

可以看到,为了美观,还给电风扇在前罩上面加了logo。没错,这个电风扇是一个有品牌的电风扇,该有档次也不能掉,安排。

.fan__housing-front {
  transform: translate3d(-50%, -50%, calc(var(--width) * 0.11));
}
.fan__housing-front:after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  height: 35%;
  width: 35%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: var(--shade-one);
}
img {
  height: 20%;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate3d(-50%, -50%, 1px);
  filter: grayscale(1);
  opacity: 0.5;
}

来吧,代码完成了。看看最后完工的电风扇的效果图。如下:

image.png

是不是和现实中的电风扇没啥区别,甚至我觉得还比真实的电风扇还夺目。主要这配色很清新,让人觉得甚是撩人🤌。

八、转动和控制器

电风扇实现了,现在要想法让它转起来,这个示例的标题叫纯CSS3电风扇动画DEMO演示,如果用JS来控制它转动就名不副实了。既然纯CSS3,那就用CSS相关的属性去实现吧。

8.1 转动

转动主要有2个部件需要转动,那就是电风扇的转子和叶片。至于扇头的转动是由于转子的转动带着扇头转动的,所以控制了转子就能控制扇头。

8.1.1 转子转动

首先来让转子转起来→如果细心的小伙伴可以看到,上面的有些样式代码已经把部分动画代码展示出来了,只是没有去绑定动画事件。动画属性animation需要通过@keyframes规则,才能够创建动画。

@keyframes规则要求我们以百分比来规定改变发生的时间,或者通过关键词"from""to",等价于0%100%0%是动画的开始时间,100%动画的结束时间。为了获得最佳的浏览器支持,应该始终定义0%100%选择器。

那上面展示的样式代码就不再赘述,直接来看使用@keyframes创建的动画代码。如下:

@-webkit-keyframes fan {
  0%, 5% {
    transform: translate3d(0, 0, calc(var(--width) * -0.25)) rotateY(calc(var(--rotation, 0) * 1deg));
  }
  95%, 100% {
    transform: translate3d(0, 0, calc(var(--width) * -0.25)) rotateY(calc(var(--rotation, 0) * -1deg));
  }
}
@keyframes fan {
  0%, 5% {
    transform: translate3d(0, 0, calc(var(--width) * -0.25)) rotateY(calc(var(--rotation, 0) * 1deg));
  }
  95%, 100% {
    transform: translate3d(0, 0, calc(var(--width) * -0.25)) rotateY(calc(var(--rotation, 0) * -1deg));
  }
}

fan就是animation语法中的name;而@-webkit-keyframes这样的写法是为了兼容浏览器的缘故。

image.png

来看看,电风扇转子转动之后的效果。如下:

动画3.gif

8.1.2 叶片转动

好,上面已成功的让电风扇左右摇摆了,下面就是来让叶片也转动起来。按照上面转子转动的套路,照猫画虎得到如下代码:

@-webkit-keyframes rotate {
  from {
    transform: translate3d(-50%, -50%, -1px) rotate(0deg);
  }
  to {
    transform: translate3d(-50%, -50%, -1px) rotate(360deg);
  }
}
@keyframes rotate {
  from {
    transform: translate3d(-50%, -50%, -1px) rotate(0deg);
  }
  to {
    transform: translate3d(-50%, -50%, -1px) rotate(360deg);
  }
}

就上面所示的代码得到下图所示的效果。如下:

动画4.gif

8.2 控制器

电风扇算是转起来啦!但是感觉风力是有的小呢,本就炎热的天气再看到如此风力的电风扇,反而会让人更加烦躁。所以为了避免电风扇被砸,下面就来为电风扇加上控制器,用于控制电风扇的开和关,还有就是控制风力的大小。

在这篇文章的第三部分的控制器环节,就已经做了引子了。就是要通过label标签的for属性将每个控制与对应的input联系起来,从而达到我们表面操作div,实际上是在操作input的效果。这样就能实现不用js也能控制每个开关的效果了。

<input type="radio" name="fan" id="zero"/>
<input type="radio" name="fan" id="one"/>
<input type="radio" name="fan" id="two"/>
<input type="radio" name="fan" id="three"/>

为电风扇安排了4个开关,所以需要4input与之对应。然后对应操作的样式代码,如下:

#zero:checked ~ .scene {
  --blade-speed: 0;
  --state: paused;
}
#zero:checked ~ .scene .fan__stalk {
  transform: translate(-50%, 25%);
}
#one:checked ~ .scene {
  --blade-speed: 1;
  --state: running;
}
#one:checked ~ .scene .fan__control:nth-of-type(2) {
  transition: transform 0.1s cubic-bezier(0, 1.4, 0.2, 1.4);
  transform: translate(0, 50%);
}
#two:checked ~ .scene {
  --blade-speed: 0.5;
  --state: running;
}
#two:checked ~ .scene .fan__control:nth-of-type(3) {
  transition: transform 0.1s cubic-bezier(0, 1.4, 0.2, 1.4);
  transform: translate(0, 50%);
}
#three:checked ~ .scene {
  --blade-speed: 0.25;
  --state: running;
}
#three:checked ~ .scene .fan__control:nth-of-type(4) {
  transition: transform 0.1s cubic-bezier(0, 1.4, 0.2, 1.4);
  transform: translate(0, 50%);
}

可以看到,控制电风扇转动和风力的大小是通过设置--blade-speed--state实现的。要把电风扇关了就把转速设为0,然后状态设为暂停;而电风扇风速为1时,则为第1个档位效果;电风扇风速为0.5时,则为第2个档位效果;电风扇风速为0.25时,则为第3个档位效果。如果要为电风扇默认开启一个档位就给input设置一个checked属性,如下:

<!-- 默认开启电风扇并设置为第1档位 -->
<input type="radio" name="fan" id="one" checked="true"/>  

到这里,电风扇的整个实现就完成了。最后的效果就和背景中展示的一样,我们还是来看看实际效果吧。如下:

动画2.gif

九、码上掘金

第一次使用码上掘金发布代码片段,挺好,又学会一个技能了💪👍。大家可以到马上掘金看到该示例的效果。

十、临时需求

应评论区朋友们的要求:需要让风扇不转动,一直对着吹的需求。我也是马上对代码进行了改动,最终实现了让风扇可以不转动。具体如下:

<label class="fan__control" for="four">
    <div class="cuboid control">
        <div class="cuboid__side"></div>
        <div class="cuboid__side"></div>
        <div class="cuboid__side"></div>
        <div class="cuboid__side"></div>
        <div class="cuboid__side"></div>
        <div class="cuboid__side"></div>
    </div>
</label>

在控制器容器中增加了一个用于控制电风扇不转动的控制开关,即上述代码。

然后需要对应加上一个控制它的单选input,至于怎么将两者关联起来上述文章已详细讲解,这里就不再展开了。如下:

<input type="radio" name="fan" id="four"/>

接下来就是去对样式代码进行改造,首先要让新增的开关出现,那就需要改动此处代码。如下:

image.png

然后就是来写控制代码,也很简单。原理就是开关触发,扇头不转动,就业片转动即可。如下:

#four:checked ~ .scene {
  --blade-speed: 0.25;
  --state: running;
}
#four:checked ~ .scene .fan__head {
  animation: initial;
  transform: translate(-20%, -20%) rotateY(-30deg);
}
#four:checked ~ .scene .fan__control:nth-of-type(5) {
  transition: transform 0.1s cubic-bezier(0, 1.4, 0.2, 1.4);
  transform: translate(0, 50%);
}
.fan__control:nth-of-type(5) .control {
  --shade-three: #f77a5b;
  --shade-five: #dd512e;
  --shade-six: #dd562d;
  --shade-eight: #da330a;
}

这里为了让各位朋友吹爽,我设置的风扇不转动时是最大风力。让朋友们一次吹个够,哈哈哈。这样编写代码之后就可以让其控制风扇不转动了。效果如下:

动画1.gif

后面我还知道你们肯定会提不转动时要可以改变风力大小,这个需求我就不做了,因为现在用于控制电风扇的开关都是单选,没有使用多选,只能满足一种情况。如果非要加,那就只有继续安排控制开关,电风扇不转动时也是3个风力档位,感兴趣的朋友可以自己去尝试尝试。为了让朋友们明显地知道哪个按钮是控制不转的,我特意加了红色色系用于区分,这也是评论区朋友要求的,这样确实能让体验提升,再次感谢建议哦,栓Q🌹。

好,电风扇不转动的需求就到这里,码上掘金中也更新了最新代码,大家可以去看最新效果。谢谢你因为有你温暖了四季~~~

总结

通过介绍电风扇这个示例的实现过程,还是让我深有感触的。从中还是获得了一些我之前不曾知道的知识和技术点,也对一些我知道的知识点进行了温顾。比如:

  1. 原生CSS中是可以通过:root的方式来声明全局变量的,而变量则是通过--xxx声明的;使用则是使用var(--xxx),如:background: var(--bg);
  2. label中的for属性可以将label和表单控件绑定在一起;
  3. :nth-child()不限制类型,:nth-of-type()限制类型;
  4. 加深animation动画属性的使用方法;
  5. 了解@keyframes规则;
  6. 可以通过inputchecked属性实现关联div的选中效果;
  7. 学会使用码上掘金进行代码片段的插入。

所以我希望我的这次分享也能让你从中获得或多或少的技术知识,而不是单纯的看了就算了,更不想你直接将代码拷贝之后看到效果后乐呵乐呵就算了。分享的目的还是为了让我们查漏补缺,同时去提高我们自身的业务水平和技术能力。

后语

小伙伴们,如果觉得本文对你有些许帮助,点个👍或者➕个关注再走呗^_^ 。另外如果本文章有问题或有不理解的部分,欢迎大家在评论区评论指出,我们一起讨论共勉。

src=http___p6.itc.cn_q_70_images03_20210104_70f8545500034a5bae5f1695a7ce3da0.jpeg&refer=http___p6.itc.webp