前端开发中的 CSS @keyframes 动画指南

文章目录

一、什么是 @keyframes

@keyframes 是 CSS 中用于定义动画关键帧序列的规则。它描述了一个元素在动画过程中,其样式如何随时间变化。

transition(仅支持起止两点)不同,@keyframes 支持:

  • 多个中间状态(如 0% → 30% → 70% → 100%)
  • 循环播放
  • 方向控制
  • 精确的时间轴定义

1.1 基本语法

css 复制代码
@keyframes animation-name {
  from { /* 起始样式 */ }
  to   { /* 结束样式 */ }
}

或使用百分比(更灵活):

css 复制代码
@keyframes slideIn {
  0%   { transform: translateX(-100px); opacity: 0; }
  100% { transform: translateX(0);     opacity: 1; }
}

规范说明(CSS Animations §3.1):

  • from 等价于 0%to 等价于 100%
  • 关键帧顺序在源码中可任意排列,浏览器按百分比数值自动排序
  • 若未定义 0%100%,浏览器会自动推断缺失端点(取元素当前计算样式)

alternate: 0% → 100% → 0%

1.2 应用动画

仅定义 @keyframes 不会产生效果。必须通过 animation 属性将其绑定到元素:

css 复制代码
.element {
  animation: slideIn 0.4s ease-out forwards;
}

二、animation 八大属性详解

CSS 定义了 8 个独立属性,共同控制动画行为。它们既可分开书写,也可通过 animation 简写合并(animation-play-state 除外)。

⚠️ 重要规范:animation 简写的最小有效形式

根据 W3C CSS Animations Level 1 §4

  • animation 简写中,只有 animation-nameanimation-duration 是逻辑上必需的
  • 实际最小可见 动画必须包含 name + duration
  • 若只写名称(如 animation: fadeIn;),则 duration 使用默认值 0s,动画瞬间完成,肉眼不可见
  • 浏览器按固定顺序解析简写值:
    1. <animation-name>
    2. 第一个 <time>duration
    3. 第二个 <time>delay
    4. <timing-function>
    5. <iteration-count>
    6. <direction>
    7. <fill-mode>
  • animation-play-state 永远不会被简写解析,必须单独声明

✅ 正确示例:

css 复制代码
/* 最小有效(可见)动画 */
.element { animation: slideIn 0.4s; }

/* 完整控制 */
.element { animation: bounce 0.6s ease-in 0.1s infinite alternate forwards; }

❌ 常见错误:

css 复制代码
/* 错误:只写名称 → duration = 0s → 动画不可见 */
.element { animation: fadeIn; }

2.1 animation-name

  • 作用 :指定要应用的 @keyframes 名称
  • 默认值none
  • 是否可简写:✅ 是
典型用法
css 复制代码
.element {
  animation-name: fadeIn;
}

/* 多动画叠加 */
.element {
  animation-name: float, pulse;
  animation-duration: 3s, 2s;
  animation-iteration-count: infinite, infinite;
}
工程提示
  • 名称拼错或未定义 → 动画静默失效(无报错)
  • 名称区分大小写

2.2 animation-duration

  • 作用:单次动画循环的持续时间
  • 默认值0s(动画瞬间完成,不可见)
  • 是否可简写:✅ 是
典型用法
css 复制代码
.button:hover {
  animation: ripple 0.2s linear;
}
.modal {
  animation: slideIn 0.4s ease-out forwards;
}
工程提示
  • 必须带单位animation-duration: 1; ❌ 无效
  • 推荐时长:
    • 微交互:150ms -- 300ms
    • 页面动效:300ms -- 600ms
  • 避免 >1s(用户感知为卡顿)

2.3 animation-timing-function

  • 作用:定义关键帧之间的速度变化曲线(缓动函数)
  • 默认值ease
  • 是否可简写:✅ 是
常用值说明
说明
linear 匀速运动,无加速/减速,机械感强(适合进度条、跑马灯)
ease 默认缓动:快入慢出(类似 ease-out,但起始稍缓)
ease-in 慢启动 → 快结束,模拟物体从静止开始加速
ease-out 快启动 → 慢停止,最常用,模拟自然减速停稳
ease-in-out 慢 → 快 → 慢,适合对称动画(如呼吸、弹跳)
cubic-bezier(x1,y1,x2,y2) 自定义贝塞尔曲线,可实现弹性、过冲等高级效果
step-start 动画开始瞬间跳到最终状态(0% → 100% 无过渡)
step-end 动画结束瞬间才变化(常用于打字机、帧动画)
steps(5)steps(5, end) 分 5 步完成,中间无渐变;第二参数可选 start/end(默认 end
典型用法
css 复制代码
.card {
  animation: popIn 0.5s cubic-bezier(0.68, -0.55, 0.27, 1.55) forwards;
}
.typewriter {
  animation: type 2s steps(20, end) forwards;
}
@keyframes type {
  to { width: 100%; }
}
工程提示
  • 使用 cubic-bezier.com 可视化生成曲线
  • steps() 适用于 sprite 动画或逐字显示

2.4 animation-delay

  • 作用:动画开始前的等待时间
  • 默认值0s
  • 是否可简写:✅ 是
典型用法
css 复制代码
/* 错峰出场 */
.item:nth-child(1) { animation-delay: 0.0s; }
.item:nth-child(2) { animation-delay: 0.1s; }

/* 负延迟:跳过开头 */
.synced {
  animation: myAnim 1s ease-out -0.3s;
}
工程提示
  • 负值表示"从动画中间开始"
  • delay 期间元素保持原始样式,除非设置 fill-mode: backwards

2.5 animation-iteration-count

  • 作用:动画播放次数
  • 默认值1
  • 是否可简写:✅ 是
常用值说明
说明
1 播放 1 次(默认)
3 播放 3 次
infinite 无限循环(⚠️ 注意性能与无障碍)
典型用法
css 复制代码
.spinner {
  animation: rotate 1s linear infinite;
}
.alert {
  animation: blink 0.5s ease-in-out 3;
}
工程提示
  • infinite 动画在后台仍可能运行 → 建议监听 visibilitychange 暂停
  • 必须响应 prefers-reduced-motion
css 复制代码
@media (prefers-reduced-motion: reduce) {
  .spinner { animation: none; }
}

2.6 animation-direction

  • 作用:控制每次迭代的播放方向
  • 默认值normal
  • 是否可简写:✅ 是
常用值说明
说明
normal 每次均 0% → 100%
reverse 每次均 100% → 0%
alternate 奇数次 0%→100%,偶数次 100%→0%(来回摆动)
alternate-reverse 奇数次 100%→0%,偶数次 0%→100%
典型用法
css 复制代码
.pendulum {
  animation: swing 1s ease-in-out infinite alternate;
}
@keyframes swing {
  0% { transform: rotate(-15deg); }
  100% { transform: rotate(15deg); }
}
工程提示
  • alternate 是实现"自然摆动"的最简方式
  • iteration-count: 1 无效(等同于 normal

2.7 animation-fill-mode

  • 作用 :控制动画之外(开始前 / 结束后)的样式状态
  • 默认值none
  • 是否可简写:✅ 是
常用值说明
说明
none 默认行为:动画结束后恢复原始 CSS 样式(易导致"闪回")
forwards 保持最后一帧状态(⭐ 淡入/滑入后必须用!)
backwards delay 期间就应用第一帧样式(避免 delay 时显示未初始化状态)
both 同时启用 forwards + backwards
典型用法
css 复制代码
/* 正确:淡入后保持可见 */
.modal {
  opacity: 0;
  animation: fadeIn 0.3s ease-out forwards;
}
@keyframes fadeIn {
  to { opacity: 1; }
}

/* 错误:动画结束后消失! */
.modal-wrong {
  opacity: 0;
  animation: fadeIn 0.3s ease-out; /* 缺少 forwards */
}
工程提示
  • 凡是用于"显示/隐藏"类动画,几乎 always 用 forwards
  • backwards 常与 delay 配合使用

2.8 animation-play-state

  • 作用:控制动画当前是运行还是暂停
  • 默认值running
  • 是否可简写 :❌ 否!必须单独声明
典型用法
css 复制代码
/* 悬停暂停 */
.rotating-icon {
  animation: rotate 2s linear infinite;
}
.rotating-icon:hover {
  animation-play-state: paused;
}

/* 焦点暂停背景 */
input:focus ~ .bubbles {
  animation-play-state: paused;
}
工程提示
  • 暂停时时间线冻结,恢复后继续(不重置)

  • 性能极佳:不触发重排/重绘

  • 可被 JavaScript 动态控制:

    js 复制代码
    el.style.animationPlayState = document.hidden ? 'paused' : 'running';
  • 常用于悬停查看、节能、减少干扰


三、性能与最佳实践

3.1 仅动画合成属性

为保证 60fps 流畅度,只应动画以下属性

  • transformtranslate, scale, rotate
  • opacity

✅ 这两类属性由 GPU 合成,不触发 Layout/Paint

❌ 避免:width, height, top, left, margin, padding, font-size

3.2 响应用户偏好

尊重无障碍设置:

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

✅ 符合 WCAG 2.1 AA 标准

3.3 调试工具

Chrome DevTools → Elements → 选中元素 → Styles 中点击 animation 旁 🎞️ 图标

→ 可逐帧查看、减速、导出动画


四、常见错误与解决方案

问题 原因 解决方案
动画无效果 duration0 或名称拼错 检查 animation-duration > 0name 匹配
动画结束后元素消失 未设 fill-mode: forwards 添加 forwards
卡顿掉帧 动画了非合成属性 改用 transform / opacity
无限循环耗电 未处理后台标签页 监听 visibilitychange 暂停动画
多动画错乱 animation 简写中属性数量不匹配 确保各子属性数量与动画名数量一致
play-state 无效 写进了 animation 简写 必须单独写:animation-play-state: paused;
动画看不见 只写了 animation: name; 补全 durationanimation: name 0.3s;
相关推荐
LYFlied2 小时前
前端技术风险防控:以防为主,防控结合
前端·工程化·技术风险防控
宁雨桥2 小时前
前端跨页面通信:从基础到工程化的全面指南
前端·vue.js·react.js
梵尔纳多2 小时前
electron 安装
前端·javascript·electron
心.c2 小时前
初步了解Next.js
开发语言·前端·javascript·js
挫折常伴左右2 小时前
初见HTML
前端·html
阿蓝灬2 小时前
详述单点登录(SSO)
前端
灵感__idea2 小时前
Hello 算法:以“快”著称的哈希
前端·javascript·算法
恋猫de小郭2 小时前
Flutter 官方正式解决 WebView 在 iOS 26 上有点击问题
android·前端·flutter
阿珊和她的猫3 小时前
CSS3新特性概述
前端·css·css3