TIL 项目实战:用 CSS Animation 打造平滑脉动背景动画
TIL 项目实战:用 CSS Animation 打造平滑脉动背景动画
导读
在 TIL 项目("Today I Learned" 系列笔记,见 README.md)的 CSS 分类中,记录了如何仅用 @keyframes 关键帧规则加 animation 简写属性,实现一个无需 JavaScript、平滑循环的脉动背景效果。本文以 css/create-a-pulsing-background-with-css-animation.md 为骨架,系统讲解关键帧定义、动画属性调优、缓动与时长选择,并结合仓库中同主题笔记 css/animate-smoothly-between-two-background-colors.md 与 CSS 性能实践,给出可直接复制运行的完整方案。
一、效果原理:一段代码看懂脉动背景
脉动背景(pulsing background)的本质是:让元素的 background-color 在一个起始颜色与另一个颜色之间往复过渡。整个过程完全由浏览器渲染引擎驱动,CSS 只需声明"什么时刻是什么颜色",其余插值计算交给引擎完成。
原文档给出的核心关键帧规则如下:
@keyframes pulse {
0%, 100% {
background-color: #f56a3f;
}
50% {
background-color: #9e42b0;
}
}
关键点拆解:
@keyframes pulse:定义一个名为pulse的关键帧动画,名字可自由命名,后续通过animation属性引用;0%, 100%:动画起点与终点合并声明,background-color为#f56a3f(暖橙色);50%:动画进行到一半时颜色切换为#9e42b0(紫色)。
浏览器会在 0% → 50% → 100% 三个关键帧之间自动插值,产生颜色渐变,而非生硬跳变。
二、应用到元素:animation 简写属性
定义好 @keyframes 后,需要把它挂载到目标元素上。原文档将它应用到 body,并配合无限循环与长时长:
body {
animation: pulse 20s infinite;
}
这一行是 animation 简写属性,它按顺序展开为多个子属性:
| 简写中的位置 | 对应子属性 | 本例取值 | 说明 |
|---|---|---|---|
| 1 | animation-name |
pulse |
引用 @keyframes 名字,必须一致 |
| 2 | animation-duration |
20s |
单个动画周期时长,越长越舒缓 |
| 3 | animation-iteration-count |
infinite |
无限循环;也可写具体次数如 3 |
仓库中同主题笔记 css/animate-smoothly-between-two-background-colors.md 展示了一个更精细的写法:将 0% 与 100% 分开声明、并把动画应用在带尺寸的类上:
@keyframes pulse {
0% {
background-color: red;
}
50% {
background-color: blue;
}
100% {
background-color: red;
}
}
.square1 {
animation: pulse 2s infinite;
width: 100px;
height: 100px;
}
两种写法等价:0%, 100% 合并声明与分开声明效果相同。区别在于应用范围——应用到 body 影响整页背景,应用到 .square1 这类带尺寸的元素则只影响该元素,适合做按钮、卡片、提示气泡等局部动效。
三、让"脉冲"真正平滑:时长、缓动与循环
原文档强调"以较长的时长无限循环,以获得真正的平滑手感"。这背后的可调旋钮有三个:
1. 时长(animation-duration)
- 2s 左右:节奏较快,适合小元素上的注意力吸引;
- 10s~20s:原文档采用的区间,颜色变化极其缓慢,视觉上"呼吸感"强,适合页面整体背景;
- 配合
0%与100%颜色相同的特点,循环接缝处无色差,肉眼几乎无法察觉动画重新开始。
2. 缓动(animation-timing-function)
默认缓动为 ease。想让颜色变化全程匀速,可显式指定:
body {
animation: pulse 20s infinite ease-in-out;
}
ease-in-out:两头慢、中间快,模拟呼吸的起落感;linear:全程匀速,适合色彩渐变的匀速呼吸灯;steps(n):阶梯式跳变,若想要"离散切换颜色"而非平滑渐变,可以用它代替默认插值。
3. 循环次数
infinite 表示无限循环。若只想让"脉冲"发生固定几次再停下,替换为具体数值即可,例如 animation: pulse 2s 3;(3 次后停在 100% 关键帧状态)。
四、进阶:从固定颜色到自定义变量
写死两个十六进制色值虽然直观,但在大型项目中维护不便。可以将颜色抽成 CSS 自定义属性,让动画规则与配色解耦:
:root {
--pulse-start: #f56a3f;
--pulse-end: #9e42b0;
}
@keyframes pulse {
0%, 100% {
background-color: var(--pulse-start);
}
50% {
background-color: var(--pulse-end);
}
}
修改配色时只需更新 :root 中的两个变量,动画本身无需改动。这一思路与仓库中 css/define-css-custom-properties-with-scss-variables.md 记录的自定义属性实践一脉相承。
五、性能与可访问性注意点
1. 动效属性选择
background-color 属于 paint 类属性,在动画期间浏览器需要持续重绘元素区域。单元素、低频次(如 20s 一个周期)的脉动背景在现代浏览器上开销可忽略;但若在大量元素上高频动画 background-color,则可能造成掉帧。仓库中的 css/filter-blur-requires-expensive-calculation.md 记录了同类教训:昂贵的 CSS 效果在"数量多"的场景下会显著拖累渲染。若需要高性能方案,可考虑用 opacity 叠加静态渐变层来模拟呼吸效果,因为 opacity 属于合成器(compositor)友好的属性,动画更省资源。
2. 尊重用户偏好
对某些用户而言,持续脉动的动画可能引发不适。建议在实现中加入 prefers-reduced-motion 媒体查询降级处理:
body {
animation: pulse 20s infinite;
}
@media (prefers-reduced-motion: reduce) {
body {
animation: none;
background-color: #f56a3f;
}
}
当用户在系统中开启"减少动态效果"时,动画被关闭、退回静态底色。仓库中 css/apply-styles-based-on-dark-mode-preferences.md 展示了同类媒体查询(prefers-color-scheme)的用法,二者语法一致,可以对照学习。
六、完整可运行示例
综合以上要点,给出一个完整示例,可直接放入 HTML 的 <style> 或独立 CSS 文件中:
:root {
--pulse-start: #f56a3f;
--pulse-end: #9e42b0;
}
@keyframes pulse {
0%, 100% {
background-color: var(--pulse-start);
}
50% {
background-color: var(--pulse-end);
}
}
body {
/* 名称 时长 缓动 循环次数 */
animation: pulse 20s ease-in-out infinite;
min-height: 100vh;
}
@media (prefers-reduced-motion: reduce) {
body {
animation: none;
background-color: var(--pulse-start);
}
}
小结
通过 @keyframes 定义起点与终点关键帧、再用 animation 简写属性(名称 + 时长 + 循环次数)挂载到目标元素,即可在十几行 CSS 内得到平滑的无限脉动背景。微调时长与缓动可以控制节奏,引入自定义属性和 prefers-reduced-motion 则让方案更易维护、更友好。本主题的原始记录见 css/create-a-pulsing-background-with-css-animation.md,可结合 css/animate-smoothly-between-two-background-colors.md 与 css/style-a-background-with-a-linear-gradient.md 继续延伸学习背景相关的其他技巧。