跳到主要内容

CSS 动画与过渡

CSS 动画和过渡让网页元素能够平滑地从一个状态变化到另一个状态,为用户界面增添生机和反馈。恰当的动画可以提升用户体验,引导用户注意力,但过度使用则会分散注意力、影响性能。这一章将介绍如何合理地使用 CSS 动画和过渡。

CSS 过渡(Transition)

过渡是最简单的动画形式,它让 CSS 属性的变化在一段时间内平滑进行,而不是立即生效。

基本语法

.element {
transition: property duration timing-function delay;
}

transition-property 过渡属性

指定哪些 CSS 属性参与过渡:

/* 单个属性 */
.box {
transition-property: width;
}

/* 多个属性 */
.box {
transition-property: width, height, opacity;
}

/* 所有属性 */
.box {
transition-property: all;
}

不是所有 CSS 属性都能过渡。可过渡的属性通常是数值类型的,如:

  • 尺寸:widthheightmarginpadding
  • 颜色:colorbackground-colorborder-color
  • 透明度:opacity
  • 变换:transform
  • 阴影:box-shadowtext-shadow

不可过渡的属性包括 displayvisibility(只能离散变化)、position 等。

transition-duration 过渡时长

指定过渡持续的时间:

.fast { transition-duration: 0.2s; }
.normal { transition-duration: 0.3s; }
.slow { transition-duration: 0.5s; }
.multiple { transition-duration: 0.3s, 0.5s; } /* 多个属性不同时长 */

通常 0.2s 到 0.5s 的时长最适合用户界面动画,既能感知到变化又不会太慢。

transition-timing-function 时间函数

控制过渡过程中速度的变化:

.linear { transition-timing-function: linear; }      /* 匀速 */
.ease { transition-timing-function: ease; } /* 默认:慢-快-慢 */
.ease-in { transition-timing-function: ease-in; } /* 慢开始 */
.ease-out { transition-timing-function: ease-out; } /* 慢结束 */
.ease-in-out { transition-timing-function: ease-in-out; } /* 慢开始和结束 */

/* 自定义贝塞尔曲线 */
.custom { transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1); }

/* 步进函数 */
.steps { transition-timing-function: steps(4, end); }

ease 是最常用的时间函数,自然流畅。ease-out 适合元素进入视野的动画,ease-in 适合元素离开视野的动画。

transition-delay 过渡延迟

指定过渡开始前的等待时间:

.immediate { transition-delay: 0s; }
.delayed { transition-delay: 0.5s; }
.staggered { transition-delay: 0s, 0.1s, 0.2s; } /* 错开动画 */

简写语法

.box {
/* 完整简写 */
transition: width 0.3s ease 0s;

/* 省略延迟 */
transition: width 0.3s ease;

/* 省略时间函数(使用默认 ease) */
transition: width 0.3s;

/* 多个属性 */
transition: width 0.3s, height 0.5s, opacity 0.2s;
}

常见过渡效果

按钮悬停效果

.button {
background-color: #3498db;
color: white;
transition: background-color 0.3s, transform 0.2s;
}

.button:hover {
background-color: #2980b9;
transform: translateY(-2px);
}

链接下划线动画

.link {
position: relative;
color: #333;
text-decoration: none;
}

.link::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 0;
height: 2px;
background: #3498db;
transition: width 0.3s;
}

.link:hover::after {
width: 100%;
}

卡片悬停效果

.card {
transition: transform 0.3s, box-shadow 0.3s;
}

.card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
}

展开/收起效果

.expandable {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out;
}

.expandable.open {
max-height: 500px;
}

CSS 动画(Animation)

动画比过渡更强大,可以定义多个关键帧,实现更复杂的动画序列,还可以自动播放、循环播放。

@keyframes 关键帧

使用 @keyframes 定义动画序列:

/* 从...到... */
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

/* 百分比关键帧 */
@keyframes bounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-20px);
}
}

/* 多个关键帧 */
@keyframes pulse {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.1);
opacity: 0.8;
}
100% {
transform: scale(1);
opacity: 1;
}
}

from 等价于 0%to 等价于 100%。如果不指定 0%100%,浏览器会使用元素的初始状态或最终状态。

animation 属性

animation-name:指定要使用的关键帧名称

.element {
animation-name: fadeIn;
}

animation-duration:动画持续时间

.element {
animation-duration: 1s;
}

animation-timing-function:时间函数

.element {
animation-timing-function: ease-in-out;
}

animation-delay:动画延迟

.element {
animation-delay: 0.5s;
}

animation-iteration-count:动画播放次数

.once { animation-iteration-count: 1; }      /* 播放一次 */
.loop { animation-iteration-count: infinite; } /* 无限循环 */
.custom { animation-iteration-count: 3; } /* 播放三次 */

animation-direction:动画播放方向

.normal { animation-direction: normal; }       /* 正向播放 */
.reverse { animation-direction: reverse; } /* 反向播放 */
.alternate { animation-direction: alternate; } /* 正向再反向交替 */
.alternate-reverse { animation-direction: alternate-reverse; }

animation-fill-mode:动画执行前后的样式

.none { animation-fill-mode: none; }          /* 默认:动画前后恢复初始状态 */
.forwards { animation-fill-mode: forwards; } /* 保持最后一帧状态 */
.backwards { animation-fill-mode: backwards; } /* 动画前应用第一帧状态 */
.both { animation-fill-mode: both; } /* 同时应用 forwards 和 backwards */

animation-play-state:动画播放状态

.running { animation-play-state: running; }  /* 播放 */
.paused { animation-play-state: paused; } /* 暂停 */

简写语法

.element {
animation: name duration timing-function delay iteration-count direction fill-mode;
}

/* 示例 */
.box {
animation: fadeIn 0.5s ease-out 0.2s 1 forwards;
}

/* 无限循环动画 */
.spinner {
animation: spin 1s linear infinite;
}

常见动画效果

淡入

@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}

.fade-in {
animation: fadeIn 0.5s ease-out;
}

滑入

@keyframes slideIn {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}

.slide-in {
animation: slideIn 0.5s ease-out;
}

弹跳

@keyframes bounce {
0%, 20%, 50%, 80%, 100% {
transform: translateY(0);
}
40% {
transform: translateY(-30px);
}
60% {
transform: translateY(-15px);
}
}

.bounce {
animation: bounce 1s;
}

旋转加载

@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}

.spinner {
width: 40px;
height: 40px;
border: 3px solid #f3f3f3;
border-top: 3px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}

脉冲

@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}

.pulse {
animation: pulse 2s infinite;
}

抖动

@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}

.shake {
animation: shake 0.5s;
}

性能优化

使用 transform 和 opacity

transformopacity 的动画由 GPU 加速,不会触发重排(reflow),性能最好:

/* 推荐 */
.good {
transition: transform 0.3s, opacity 0.3s;
}
.good:hover {
transform: translateX(10px);
opacity: 0.8;
}

/* 避免 */
.bad {
transition: left 0.3s, top 0.3s, width 0.3s;
}
.bad:hover {
left: 10px;
top: 10px;
width: 200px;
}

will-change 提示

will-change 提示浏览器某个属性即将变化,让浏览器提前优化:

.will-animate {
will-change: transform, opacity;
}

但不要滥用,只在需要时使用,动画结束后移除:

/* 动画前添加 */
.element.animating {
will-change: transform;
}

/* 动画后移除 */
.element:not(.animating) {
will-change: auto;
}

减少动画偏好

尊重用户的系统设置,减少不必要的动画:

@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}

content-visibility

对于不在视口内的动画元素,可以使用 content-visibility: auto 跳过渲染:

.offscreen-animation {
content-visibility: auto;
contain-intrinsic-size: 500px;
}

过渡与动画的选择

使用过渡的场景

  • 状态切换(悬停、聚焦、激活)
  • 简单的起始和结束状态
  • 用户触发的交互反馈

使用动画的场景

  • 复杂的多帧动画序列
  • 自动播放的动画
  • 循环动画
  • 需要精确控制每一帧的场景

小结

这一章我们学习了:

  • CSS 过渡的四个属性:property、duration、timing-function、delay
  • 常见过渡效果的实现
  • @keyframes 关键帧动画的定义
  • animation 的各个属性
  • 常见动画效果的实现
  • 动画性能优化的方法

动画应该增强用户体验,而不是分散注意力。记住几个关键点:

  • 使用 transformopacity 实现高性能动画
  • 动画时长通常在 0.2s 到 0.5s 之间
  • 使用 ease-outease-in-out 时间函数
  • 尊重用户的减少动画偏好
  • 不要为了动画而动画,每个动画都应该有目的

恭喜你完成了 HTML/CSS 教程的学习!你已经掌握了网页开发的核心技术。继续练习,多动手写代码,你会发现 HTML 和 CSS 的世界远比想象中更广阔。