💡✨ 包教包会!纯 CSS 实现步进环绕跑马灯效果

4,834 阅读9分钟

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

前言

跑马灯大家都见过吧?只是单单的跑马灯有点太炫了,看久了眼睛就亮瞎了,今天我带大家手把手用 CSS 制作一个百看不腻的跑马灯,不信你往下瞅瞅?

本文将会带大家学到以下知识点:

  1. 垂直水平居中方式
  2. text-shadow 的用法
  3. CSS 变量的简单应用
  4. 旋转属性 rotate 的用法
  5. transform-origin 改变旋转的原点
  6. animationsteps 的用法

重置样式

首先还是老规矩,我们先将样式重置一下,避免不同浏览器的效果不一致。

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

ps:通配符会匹配页面上的 所有元素 ,相当于将整颗 DOM 树都进行了遍历,效率很低 ,大家日常开中不要这么写,咱们写 demo 自己知道就好了。

背景板

我们会后续的效果会展示在视口中央位置,因此,为了撑开 body 的高度,我们需要将它的高度设置为 100vh,也就是与视口同高。

其次,在 body 元素 宽高确定 的情况下,我们可以使用 flex 布局来实现 水平垂直居中 的效果。

body {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    background-color: #222;
}

到这里为止都比较基础,但是小伙伴们千万别不重视,细节决定成败。

文字居中

接下来我们绘制文字,这里我通过 h3 标签,内嵌 span 标签实现的。这样刚好用上了 h3 的粗标题效果。

<h3>CatWatermelon is <span>handsome</span></h3>

接下来我们要实现文字居中的效果了,这时有的小伙伴会问:刚刚上文不是已经让 body 设置了水平垂直居中吗,还要做?

上文确实设置了,但是我们想要的效果是文字叠加在特效上的,什么意思呢?我们看看下面这张图:

捕获.PNG

如果仅靠上文的手段,最后的成品会是左图那样,特效在上,文字在下,而我们想要的是文字和特效重叠的效果,那么这就必须让文字绝对的居中了。

这里我们要用到另一个水平垂直居中手段:绝对定位+transform。

h3 {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    ...
}

什么原理呢?

捕获1.PNG

我们看看上图,这是正常 left: 50%top: 50% 的效果,左边和上面都偏了点,肯定不是我们想要的,除非能让它左边上面都“回去”半个身位。诶,刚刚的 transform 难道就是这个作用?恭喜你,答对了!

我们日常开发中经常使用这套组合去实现水平垂直居中的效果。话不多说,我们回到正题。

发光文字

接下来我们要让 span 标签内的文字“发光”了,具体怎么实现呢?

不知道大家平时开发中 box-shadow 属性用的多不多,我们平时用它进行阴影的绘制,也可以模拟发光效果,不过它是针对 盒子模型 的,也就是整个标签。CSS3 中推出了一个新的属性:text-shadow ,它的效果和 box-shadow 很相似,不同的是前者用于 文字 ,后者用于 盒子

为了防止有的小伙伴还是没明白,我做了个动图:

20221011_133040.gif

从图中可以看到 text-shadowbox-shadow 很相似,我们的文字发光效果实际上是用 5 个阴影模拟的。

 h3 span {
    color: #fff;
    font-weight: 500;
    text-shadow: 0 0 10px #fff,
    0 0 20px #fff,
    0 0 30px #fff,
    0 0 40px #fff,
    0 0 50px #fff;
}

环绕方块

接下来是比较难理解的一个效果了,但是理解了很好做。

我们要做一些环绕文字的方块,我给大家画个示意图。

演示文稿4.gif

原理就是将 12 个正方形左上角都放置一个方块,然后每个正方形都进行一定角度的旋转,具体旋转多少度呢?我们知道一圈是 360°,我们有 12 个正方形需要旋转,因此每个正方形的旋转角度为 360 / 12 = 30

为了方便计算,我们将 12 个方块都设置一个 CSS 变量

<div class="box">
    <div class="loader">
        <span style="--i:1"></span>
        <span style="--i:2"></span>
        ...
        <span style="--i:12"></span>
    </div>
    <h3>CatWatermelon is <span>handsome</span></h3>
</div>

小方块的外层容器我们用 span 标签来做。

 .loader {
    position: relative;
    width: 150px;
    height: 150px;
}
.loader span {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    transform: rotate(calc(30deg * var(--i)));
}

这里使用到了 rotate 属性来进行旋转,和我们刚刚的示意图一样,

接下来是小方块的制作。

.loader span::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: 20px;
    height: 20px;
    background: rgba(255, 255, 255, 0.15);
}

这里我们通过将 span 正方形进行 相对定位 ,然后伪元素模拟的小方块进行 绝对定位 ,将小方块定位到左上角的位置。

来看看现在的效果吧:

微信截图_20221011141114.png

让方块发光

接下来我们要让部分方块发光,比如每隔两个方块,就有一个方块发光。

具体怎么做呢?首先每隔两块用 CSS 语言怎么理解呢?不知道小伙伴们平时开发的时候有没有用过伪元素?first-childlast-child?它们分别表示父元素的第一个子元素以及父元素的最后一个子元素。那么我们如果要访问第二个元素或者是第三个元素,就要用到 nth-child 属性了。

nth-child(n) 接收一个参数,表示要访问第几个,值得一提的是,它可以传入一个表达式,比如我们要访问 2 的倍数,就可以使用 nth-child(2n+2),同理,我们如果要实现每隔两个访问,就要通过 nth-child(3n+3) 来实现了。

我们将小方块添加白色背景,然后发光效果我们可以使用 box-shadow,用 5 个阴影进行叠加处理。

.loader span:nth-child(3n + 3):before {
    background: #fff;
    box-shadow: 0 0 10px #fff,
    0 0 20px #fff,
    0 0 30px #fff,
    0 0 40px #fff,
    0 0 50px #fff;
}

我们看看此时的效果:

微信截图_20221011144339.png

让小方块旋转起来

看到小标题有的小伙伴就乐了:这还不简单?直接 animation + rotate 一把梭。

好像也没错,但是你真的想清楚了吗?我们先来一版看看效果。

.loader span:nth-child(3n + 3):before {
    ...
    animation: animateSquare 2s linear infinite;
}
@keyframes animateSquare {
    0%,25% {
        transform: rotate(0deg);
    }
    75%,90%,100% {
        transform: rotate(180deg);
    }
}

我们分别在 0%、25%、75%、90% 以及 100% 的时候设置不同的旋转角度,让它们有一个速度差的感觉。

20221011_144959.gif

从图中我们可以发现小方块们是旋转了没错,但是好像没有想象中那样跑到下一个发光小方块的位置。这是因为 rotate 是以原点位置来进行旋转的,正方形的原点在它的交叉轴的交点位置,因此旋转时是待在原地的。

为了实现这个效果,我们需要用到另一个属性:transform-origin

它可以改变我们的原点位置,让物体沿着新的原点进行变换。

我们试试不断加大 transform-origin 的值,看看改变它之后对我们的 rotate 效果有什么影响。

20221011_145501 (1).gif

可以发现, transform-origin 的值越大,绕的幅度就越大。

那么我们为了达到当前发光小方块旋转到下一发光小方块的效果,我们就要找一个合适的 transform-origin 值,这个值怎么计算呢?很简单,我们直接设置为正方形长度的一半,也就是 150 / 2 = 75 。我们再看看效果:

20221011_150023.gif

这就对了。我们接着走。

发光走马灯

接下来我们就要让发光小方块像走马灯一样,一个亮完后让下一个亮起来。

怎么实现呢?这里有点复杂了,我们先让它们旋转起来吧。

 .loader {
    ...
    animation: animate 24s linear infinite;
}
@keyframes animate {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }

}

只要给 .loader 类添加一个动画 animate,并让其在 24 秒内线性循环播放就好了。

20221011_151433.gif

这个旋转的动画在 24 秒内会完成绕圈 360° 的动作,我们可以看到它是一步一个脚印循规蹈矩的“走”(这里后面要考,记住了昂)。

但是这不是我们想要的效果,我们不需要它慢慢走,为了模拟走马灯,我们需要发光小方块一次跳一格(一次旋转到一定位置,使其看着像跳了一格)。为此,我们需要将 animation 中的 linear 属性换为 steps(12)

别的先不说,我们先看效果:

20221011_152540.gif

是不是每个发光小方块都一次跳一格?这是什么原理呢?

这就是 steps 的魔力。如果我们把 linear 比作一步一个脚印,那可以将 steps 比作瞬移,它省略了中间的过程。这么说可能有的小伙伴还是不懂,话不多说,上示意图好吧。

微信截图_20221011153410.png

假如我们要在 10 秒内让正方形旋转 90°,那么我们使用 linear 就大概像上图一样(省略过程),是慢慢旋转过去的;而如果我们使用 steps(4),就会每 10 / 4 秒时,截取当时正方形形态的 快照 ,共截取 4 张。

我们想要实现小方块一跳一跳的效果,就是每 24 / 2 秒时,截取一次当时状态的快照。为什么是 24 / 2 呢?因为 24 秒旋转 360°,每秒旋转 15°,而我们刚刚的小方块围绕效果是每个正方形旋转 30 * var(--i) 实现的,也就是说每个正方形的旋转度数相差 30°,为了实现瞬移的效果,我们要截取的是 30° 时的快照,也就是每 2 秒截一次,因此 steps 的参数是 12。

 .loader {
    position: relative;
    width: 150px;
    height: 150px;
    animation: animate 24s steps(12) infinite;
}

码上掘金

Github 源码

juejin-demo/animate-block-demo at main · catwatermelon/juejin-demo (github.com)

结束语

本文就到此结束了,希望大家共同努力,早日拿下 CSS 💪💪。

如果文中有不对的地方,或是大家有不同的见解,欢迎指出 🙏🙏。

如果大家觉得所有收获,欢迎一键三连💕💕。