CSS 交互与动画
约 2232 字大约 7 分钟
2026-04-16
1. 过渡(transition)
过渡是 CSS 中最基础的交互效果,通过平滑的方式将元素从一种状态变化到另一种状态。
1.1 过渡属性
| 属性 | 描述 | 值 |
|---|---|---|
transition-property | 指定要过渡的 CSS 属性 | none、all、具体属性名如 width、opacity |
transition-duration | 过渡持续时间 | 时间值,如 0.5s、500ms |
transition-timing-function | 过渡缓动曲线 | ease、linear、ease-in、ease-out、ease-in-out、cubic-bezier() |
transition-delay | 过渡延迟时间 | 时间值,如 0.5s、200ms |
/* 分别设置各属性 */
transition-property: width, opacity;
transition-duration: 0.5s, 0.3s;
transition-timing-function: ease-in-out;
transition-delay: 0.2s;1.2 transition 简写
/* 完整简写:property duration timing-function delay */
transition: all 0.5s ease-in-out 0.2s;
/* 常用简写形式 */
transition: width 0.3s ease;
transition: opacity 0.5s ease-in-out;
transition: transform 0.4s cubic-bezier(0.5, 0, 0.5, 1);1.3 触发方式
过渡需要由特定事件触发,常用方式:
- 伪类触发:
:hover、:active、:focus、:checked - 媒体查询:
@media (hover: hover)等 - JavaScript 动态修改:修改元素的类名或样式
/* 鼠标悬停时过渡 */
.button {
background-color: #3498db;
transition: background-color 0.3s ease;
}
.button:hover {
background-color: #2980b9;
}
/* 点击效果 */
.box {
transform: scale(1);
transition: transform 0.2s ease;
}
.box:active {
transform: scale(0.95);
}1.4 常见应用场景
/* 导航菜单悬停 */
.nav-link {
color: #333;
transition: color 0.3s ease, padding-left 0.3s ease;
}
.nav-link:hover {
color: #3498db;
padding-left: 8px;
}
/* 卡片浮起效果 */
.card {
transform: translateY(0);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-8px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
}
/* 图片缩放 */
.image-wrapper {
overflow: hidden;
}
.image-wrapper img {
transition: transform 0.5s ease;
}
.image-wrapper:hover img {
transform: scale(1.1);
}2. 动画(animation)
动画可以实现更复杂的视觉效果,比过渡更强大,支持暂停、循环、反向播放等。
2.1 @keyframes 定义动画
/* 基本语法 */
@keyframes animation-name {
from {
property: value;
}
to {
property: value;
}
}
/* 百分比定义关键帧 */
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/* 多关键帧 */
@keyframes bounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-20px);
}
}2.2 动画属性
| 属性 | 描述 | 值 |
|---|---|---|
animation-name | 动画名称 | @keyframes 定义的名称 |
animation-duration | 动画持续时间 | 时间值,如 1s、500ms |
animation-timing-function | 动画缓动曲线 | ease、linear、ease-in、ease-in-out、cubic-bezier() |
animation-delay | 动画延迟 | 时间值 |
animation-iteration-count | 播放次数 | 数值或 infinite |
animation-direction | 播放方向 | normal、reverse、alternate、alternate-reverse |
animation-fill-mode | 动画填充模式 | none、forwards、backwards、both |
animation-play-state | 动画播放状态 | running、paused |
/* 分别设置各属性 */
animation-name: fadeIn;
animation-duration: 1s;
animation-timing-function: ease-in-out;
animation-delay: 0.5s;
animation-iteration-count: 3;
animation-direction: alternate;
animation-fill-mode: forwards;
animation-play-state: running;2.3 animation 简写
/* 完整简写 */
animation: name duration timing-function delay iteration-count direction fill-mode;
/* 常用简写 */
animation: fadeIn 1s ease-in-out 0.5s infinite alternate forwards;
/* 多动画 */
animation: fadeIn 1s ease forwards, slideIn 0.5s ease forwards;2.4 常见示例
/* 淡入动画 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn 0.6s ease forwards;
}
/* 弹跳动画 */
@keyframes bounce {
0%, 100% {
transform: translateY(0);
animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
}
50% {
transform: translateY(-25px);
animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
}
}
.bounce {
animation: bounce 1s infinite;
}
/* 旋转加载动画 */
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.spinner {
animation: spin 1s linear infinite;
}
/* 脉冲效果 */
@keyframes pulse {
0%, 100% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.05);
opacity: 0.8;
}
}
.pulse {
animation: pulse 2s ease-in-out infinite;
}2.5 帧动画 vs 补间动画
| 特性 | 帧动画 | 补间动画 |
|---|---|---|
| 原理 | 每帧独立绘制 | 关键帧之间自动插值 |
| 控制粒度 | 每一帧都需要定义 | 只需定义关键帧 |
| 文件大小 | 较大(每帧数据) | 较小(关键帧数据) |
| 适用场景 | 复杂角色动画、特效 | UI 状态变化、简单动效 |
| 性能 | 依赖帧率 | 依赖缓动曲线 |
CSS 动画属于补间动画,由浏览器自动计算中间状态。
3. 变换(transform)
transform 属性允许对元素进行 2D 或 3D 变换,不影响文档流。
3.1 2D 变换
/* 平移 */
transform: translate(50px, 100px); /* X、Y 方向 */
transform: translateX(50px);
transform: translateY(100px);
/* 缩放 */
transform: scale(2, 0.5); /* 宽、高倍数 */
transform: scaleX(2);
transform: scaleY(0.5);
transform: scale(1.2); /* 整体缩放 */
/* 旋转 */
transform: rotate(45deg);
transform: rotate(-30deg);
/* 倾斜 */
transform: skew(30deg, 20deg);
transform: skewX(30deg);
transform: skewY(20deg);
/* 矩阵变换 */
transform: matrix(1, 0, 0, 1, 0, 0); /* 6 个参数 */
/* 组合变换 */
transform: translate(50px, 50px) rotate(45deg) scale(1.2);3.2 transform-origin
设置元素变换的起点,默认为中心点。
/* 关键字 */
transform-origin: center;
transform-origin: top;
transform-origin: bottom left;
/* 数值 */
transform-origin: 50px 50px;
transform-origin: 0 0;
/* 百分比 */
transform-origin: 50% 50%; /* 默认值 */
transform-origin: 100% 100%; /* 右下角 */
/* 组合 */
transform-origin: top left;
transform-origin: center bottom;/* 旋转动画效果对比 */
.box {
width: 100px;
height: 100px;
background: #3498db;
}
/* 以中心为圆心旋转 */
.rotate-center {
transform-origin: center;
animation: spin 2s linear infinite;
}
/* 以左上角为圆心旋转 */
.rotate-corner {
transform-origin: top left;
animation: spin 2s linear infinite;
}3.3 3D 变换
/* 3D 平移 */
transform: translate3d(50px, 100px, 200px);
transform: translateZ(50px);
/* 3D 缩放 */
transform: scale3d(1.5, 1.2, 1);
/* 3D 旋转 */
transform: rotateX(45deg);
transform: rotateY(45deg);
transform: rotateZ(45deg);
transform: rotate3d(1, 1, 1, 45deg);
/* perspective 透视 */
perspective: 1000px; /* 设置观察者距离 */
/* preserve-3d 保留 3D 空间 */
transform-style: preserve-3d;
/* backface-visibility 背面可见性 */
backface-visibility: visible; /* 默认 */
backface-visibility: hidden;/* 3D 翻转卡片 */
.card-container {
perspective: 1000px;
}
.card-face {
position: relative;
width: 200px;
height: 200px;
transform-style: preserve-3d;
transition: transform 0.6s;
}
.card-front,
.card-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
}
.card-front {
background: #3498db;
}
.card-back {
background: #e74c3c;
transform: rotateY(180deg);
}
.card-container:hover .card-face {
transform: rotateY(180deg);
}3.4 transform 与布局
/* 居中弹出 */
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* 保持宽高比的容器 */
.aspect-ratio-box {
width: 100%;
padding-bottom: 56.25%; /* 16:9 */
position: relative;
}
.content {
position: absolute;
inset: 0;
transform: scale(1); /* 防抖动 */
}4. 动画性能优化
4.1 GPU 加速
某些 CSS 属性会触发 GPU 加速,可以让动画更流畅:
/* 触发 GPU 加速的属性 */
.will-change {
will-change: transform; /* 提前告知浏览器元素将变换 */
will-change: opacity;
will-change: filter;
}
/* 组合层优化 */
.gpu-accelerated {
transform: translateZ(0);
/* 或 */
transform: translate3d(0, 0, 0);
backface-visibility: hidden;
}4.2 避免 layout thrashing
避免在动画过程中触发布局重计算:
/* 不好的写法:动画中改变宽高 */
.bad-animation {
animation: expand {
0% { width: 100px; }
100% { width: 200px; }
}
}
/* 好的写法:使用 transform */
.good-animation {
animation: expand {
0% { transform: scaleX(1); }
100% { transform: scaleX(2); }
}
}4.3 transform/opacity 优化原理
| 属性 | 渲染阶段 | 优化等级 |
|---|---|---|
transform | Compositing(合成) | 最优,不触发 layout/paint |
opacity | Compositing(合成) | 最优,只影响合成层 |
filter | Paint(绘制) | 中等,容许 GPU 加速 |
width/height | Layout(布局) | 差,触发重排 |
background-color | Paint(绘制) | 中等,触发重绘 |
/* 最优动画模式:只改变 transform 和 opacity */
.optimized-animation {
transition: transform 0.3s ease, opacity 0.3s ease;
}
.optimized-animation:hover {
transform: translateY(-10px);
opacity: 0.8;
}
/* 不要同时动画多个触发重排的属性 */
.bad {
transition: width 0.3s, height 0.3s, margin 0.3s;
}4.4 动画优化最佳实践
/* 1. 使用 will-change 提示浏览器 */
.animated-element {
will-change: transform;
transition: transform 0.3s ease;
}
/* 2. 使用 contain 隔离变化 */
.isolated {
contain: layout style paint;
}
/* 3. 批量动画减少重排 */
.list-item {
transition: transform 0.2s ease;
}
.list-item:nth-child(odd) {
transition-delay: 0.05s;
}
/* 4. 使用 requestAnimationFrame 同步动画 */5. 常用交互动画示例
5.1 弹跳效果
/* 弹跳进入 */
@keyframes bounceIn {
0% {
opacity: 0;
transform: scale(0.3);
}
50% {
transform: scale(1.1);
}
70% {
transform: scale(0.9);
}
100% {
opacity: 1;
transform: scale(1);
}
}
.bounce-in {
animation: bounceIn 0.6s ease forwards;
}
/* 上下弹跳 */
@keyframes bounceY {
0%, 100% {
transform: translateY(0);
}
25% {
transform: translateY(-20px);
}
50% {
transform: translateY(0);
}
75% {
transform: translateY(-10px);
}
}
.bounce-y {
animation: bounceY 1s ease infinite;
}5.2 淡入淡出
/* 淡入 */
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.fade-in {
animation: fadeIn 0.5s ease forwards;
}
/* 淡出 */
@keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
.fade-out {
animation: fadeOut 0.5s ease forwards;
}
/* 淡入上移 */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in-up {
animation: fadeInUp 0.5s ease forwards;
}5.3 滑动效果
/* 左侧滑入 */
@keyframes slideInLeft {
from {
opacity: 0;
transform: translateX(-100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.slide-in-left {
animation: slideInLeft 0.5s ease forwards;
}
/* 右侧滑入 */
@keyframes slideInRight {
from {
opacity: 0;
transform: translateX(100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.slide-in-right {
animation: slideInRight 0.5s ease forwards;
}
/* 底部滑入 */
@keyframes slideInBottom {
from {
opacity: 0;
transform: translateY(100%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.slide-in-bottom {
animation: slideInBottom 0.5s ease forwards;
}5.4 综合示例:卡片交互
/* 卡片组件 */
.interaction-card {
position: relative;
width: 280px;
height: 180px;
border-radius: 12px;
overflow: hidden;
cursor: pointer;
}
.interaction-card img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.interaction-card:hover img {
transform: scale(1.1);
}
.interaction-card .overlay {
position: absolute;
inset: 0;
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
opacity: 0;
transition: opacity 0.3s ease;
display: flex;
align-items: flex-end;
padding: 20px;
}
.interaction-card:hover .overlay {
opacity: 1;
}
.interaction-card .overlay span {
color: white;
font-size: 18px;
font-weight: 500;
transform: translateY(20px);
transition: transform 0.3s ease 0.1s;
}
.interaction-card:hover .overlay span {
transform: translateY(0);
}<div class="interaction-card">
<img src="https://picsum.photos/280/180" alt="示例图片" />
<div class="overlay">
<span>查看详情</span>
</div>
</div>参考文章:
