轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • JavaScript
  • TypeScript
  • Node.js
  • Vue.js
  • 前端工程化
  • 浏览器与Web API
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • JavaScript
  • TypeScript
  • Node.js
  • Vue.js
  • 前端工程化
  • 浏览器与Web API
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • JavaScript

  • TypeScript

  • Node.js

  • Vue.js

  • 工程化

  • 浏览器与Web API

    • HTML基础-语义化标签与文档结构
    • HTML基础-文本与排版标签
    • HTML基础-列表与表格
    • HTML表单-Input类型详解
    • HTML表单-表单元素与验证
    • HTML交互-多媒体元素
    • HTML工程化-模板与组件化
    • HTML工程化-性能优化
    • CSS基础-选择器与优先级
    • CSS基础-盒模型与布局基础
    • CSS基础-单位与颜色系统
    • CSS基础-文本与字体
    • CSS基础-背景、列表与表格样式
    • CSS布局-Flexbox完全指南
    • CSS布局-Grid网格布局
    • CSS布局-响应式设计实践
    • CSS进阶-动画与过渡
      • 一、CSS过渡(Transition)
        • 1.1 什么是过渡
        • 1.2 过渡属性详解
        • transition-property
        • transition-duration
        • transition-timing-function
        • transition-delay
        • 1.3 过渡简写语法
        • 1.4 实战示例
        • 按钮悬停效果
        • 卡片展开效果
      • 二、CSS动画(Animation)
        • 2.1 什么是CSS动画
        • 2.2 定义关键帧
        • 2.3 动画属性详解
        • animation-name
        • animation-duration
        • animation-timing-function
        • animation-delay
        • animation-iteration-count
        • animation-direction
        • animation-fill-mode
        • animation-play-state
        • 2.4 动画简写语法
        • 2.5 实战示例
        • 加载动画
        • 悬浮动画
        • 文字动画
      • 三、高级动画技巧
        • 3.1 多步动画
        • 3.2 动画组合
        • 3.3 动画序列
        • 3.4 动画暂停与控制
      • 四、性能优化
        • 4.1 使用transform和opacity
        • 4.2 will-change属性
        • 4.3 减少重绘和回流
        • 4.4 使用requestAnimationFrame
        • 4.5 性能测试
      • 五、最佳实践
        • 5.1 动画设计原则
        • 有目的的动画
        • 动画时长建议
        • 5.2 缓动函数选择
        • 5.3 响应式动画
        • 5.4 可访问性考虑
        • 5.5 调试技巧
      • 六、实用动画库推荐
        • 6.1 常用CSS动画库
        • Animate.css
        • Hover.css
        • 6.2 JavaScript动画库
      • 七、浏览器兼容性
        • 7.1 Transition兼容性
        • 7.2 Animation兼容性
        • 7.3 前缀处理
      • 八、总结
    • CSS进阶-渐变与阴影效果
    • CSS进阶-Transform与3D变换
    • CSS进阶-滤镜与混合模式
    • 现代CSS-CSS预处理器对比
    • 现代CSS-CSS-in-JS方案
    • 现代CSS-原子化CSS与Tailwind
    • CSS工程化-架构与规范
    • CSS工程化-性能优化
    • CSS工程化-PostCSS实战指南
    • Web API基础-DOM操作完全指南
    • Web API基础-事件处理与委托
    • Web API基础-BOM与浏览器环境
    • Web API存储-客户端存储方案
    • Web API网络-HTTP请求详解
    • Web API网络-实时通信方案
    • Web API交互-用户体验增强
    • HTML&CSS历代版本新特性
  • 前端
  • 浏览器与Web API
轩辕李
2019-07-04
目录

CSS进阶-动画与过渡

CSS动画与过渡是现代Web界面中不可或缺的一部分,它们能为用户界面增添生动性和交互感,提升用户体验。合理使用动画不仅能够吸引用户注意力,还能让界面操作更加直观。

本文将全面介绍CSS的 transition(过渡)和 animation(动画)特性,帮助你掌握从简单过渡到复杂动画的实现技巧,以及如何优化动画性能。

# 一、CSS过渡(Transition)

# 1.1 什么是过渡

过渡(Transition)允许CSS属性值在一定时间内平滑地从一个值变化到另一个值,常用于创建简单的交互效果。

基本语法:

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

属性说明:

  • transition-property:指定过渡的CSS属性名称
  • transition-duration:过渡持续时间
  • transition-timing-function:过渡时间函数(缓动效果)
  • transition-delay:过渡延迟时间

# 1.2 过渡属性详解

# transition-property

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

/* 单个属性 */
transition-property: opacity;

/* 多个属性 */
transition-property: opacity, transform;

/* 所有可过渡的属性 */
transition-property: all;

/* 不过渡任何属性 */
transition-property: none;

可过渡的常见属性:

  • 颜色:color、background-color、border-color
  • 位置:top、right、bottom、left
  • 尺寸:width、height、padding、margin
  • 变换:transform
  • 不透明度:opacity
  • 阴影:box-shadow、text-shadow

提示

并非所有CSS属性都支持过渡。通常数值类型、颜色类型的属性可以过渡,而 display、visibility 等离散型属性则不支持。

# transition-duration

设置过渡持续时间,单位为秒(s)或毫秒(ms):

/* 单个持续时间 */
transition-duration: 0.3s;

/* 多个属性不同持续时间 */
transition-property: opacity, transform;
transition-duration: 0.3s, 0.5s;

# transition-timing-function

控制过渡速度曲线(缓动函数):

/* 预定义关键字 */
transition-timing-function: ease;        /* 默认值,慢-快-慢 */
transition-timing-function: linear;      /* 匀速 */
transition-timing-function: ease-in;     /* 慢速开始 */
transition-timing-function: ease-out;    /* 慢速结束 */
transition-timing-function: ease-in-out; /* 慢速开始和结束 */

/* 贝塞尔曲线 */
transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1);

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

常用贝塞尔曲线:

效果 cubic-bezier值 说明
ease (0.25, 0.1, 0.25, 1) 平滑过渡
linear (0, 0, 1, 1) 线性过渡
ease-in (0.42, 0, 1, 1) 加速
ease-out (0, 0, 0.58, 1) 减速
ease-in-out (0.42, 0, 0.58, 1) 先加速后减速

# transition-delay

设置过渡延迟时间:

transition-delay: 0s;    /* 立即开始 */
transition-delay: 0.5s;  /* 延迟0.5秒 */
transition-delay: -0.5s; /* 负延迟:跳过前0.5秒 */

# 1.3 过渡简写语法

/* 完整写法 */
.element {
  transition-property: all;
  transition-duration: 0.3s;
  transition-timing-function: ease;
  transition-delay: 0s;
}

/* 简写形式 */
.element {
  transition: all 0.3s ease 0s;
}

/* 多个属性不同配置 */
.element {
  transition: 
    opacity 0.3s ease,
    transform 0.5s cubic-bezier(0.25, 0.1, 0.25, 1);
}

# 1.4 实战示例

# 按钮悬停效果

<html>
  <div class="demo-btn-a8c3f">
    <button class="btn-hover-a8c3f">悬停我</button>
    <button class="btn-color-a8c3f">颜色过渡</button>
    <button class="btn-scale-a8c3f">缩放效果</button>
  </div>
</html>

<style>
.demo-btn-a8c3f {
  display: flex;
  gap: 15px;
  padding: 20px;
}

.demo-btn-a8c3f button {
  padding: 12px 24px;
  font-size: 16px;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-weight: 600;
}

.btn-hover-a8c3f {
  background-color: #3498db;
  color: white;
  transition: background-color 0.3s ease, transform 0.2s ease;
}

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

.btn-color-a8c3f {
  background: linear-gradient(45deg, #e74c3c, #e67e22);
  color: white;
  transition: all 0.4s ease;
}

.btn-color-a8c3f:hover {
  background: linear-gradient(45deg, #c0392b, #d35400);
  box-shadow: 0 4px 15px rgba(231, 76, 60, 0.4);
}

.btn-scale-a8c3f {
  background-color: #2ecc71;
  color: white;
  transition: transform 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

.btn-scale-a8c3f:hover {
  transform: scale(1.1);
}
</style>

# 卡片展开效果

<html>
  <div class="card-container-b7d4e">
    <div class="card-b7d4e">
      <h3>产品标题</h3>
      <div class="card-content-b7d4e">
        <p>这是卡片的详细内容,只有在展开时才会显示。CSS过渡让展开效果更加流畅自然。</p>
      </div>
    </div>
  </div>
</html>

<style>
.card-container-b7d4e {
  padding: 20px;
  display: flex;
  justify-content: center;
}

.card-b7d4e {
  width: 300px;
  background: white;
  border-radius: 10px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  overflow: hidden;
  transition: box-shadow 0.3s ease;
}

.card-b7d4e:hover {
  box-shadow: 0 8px 20px rgba(0,0,0,0.15);
}

.card-b7d4e h3 {
  margin: 0;
  padding: 20px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
}

.card-content-b7d4e {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.5s ease, padding 0.5s ease;
  padding: 0 20px;
}

.card-b7d4e:hover .card-content-b7d4e {
  max-height: 200px;
  padding: 20px;
}

.card-content-b7d4e p {
  margin: 0;
  line-height: 1.6;
  color: #555;
}
</style>

# 二、CSS动画(Animation)

# 2.1 什么是CSS动画

CSS动画通过 @keyframes 规则定义动画序列,使用 animation 属性应用到元素上,可以创建更复杂的动画效果。

与过渡的区别:

  • 过渡需要触发(如 :hover),动画可以自动播放
  • 过渡只有开始和结束两个状态,动画可以定义多个关键帧
  • 动画可以循环播放、反向播放等

# 2.2 定义关键帧

使用 @keyframes 定义动画序列:

/* 使用百分比 */
@keyframes slidein {
  0% {
    transform: translateX(0);
    opacity: 0;
  }
  50% {
    opacity: 0.5;
  }
  100% {
    transform: translateX(100px);
    opacity: 1;
  }
}

/* 使用 from/to */
@keyframes fadein {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

# 2.3 动画属性详解

# animation-name

指定要使用的 @keyframes 名称:

animation-name: slidein;
animation-name: none; /* 不应用动画 */

# animation-duration

动画持续时间:

animation-duration: 2s;     /* 2秒 */
animation-duration: 500ms;  /* 500毫秒 */

# animation-timing-function

动画速度曲线,与 transition-timing-function 相同:

animation-timing-function: ease;
animation-timing-function: linear;
animation-timing-function: cubic-bezier(0.42, 0, 0.58, 1);

# animation-delay

动画延迟时间:

animation-delay: 0s;    /* 立即开始 */
animation-delay: 1s;    /* 延迟1秒 */
animation-delay: -1s;   /* 负延迟:跳过第1秒 */

# animation-iteration-count

动画播放次数:

animation-iteration-count: 1;        /* 播放1次 */
animation-iteration-count: 3;        /* 播放3次 */
animation-iteration-count: infinite; /* 无限循环 */

# animation-direction

动画播放方向:

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

# animation-fill-mode

动画结束后的状态:

animation-fill-mode: none;      /* 默认,不保持任何状态 */
animation-fill-mode: forwards;  /* 保持最后一帧状态 */
animation-fill-mode: backwards; /* 在延迟期间应用第一帧状态 */
animation-fill-mode: both;      /* 同时应用forwards和backwards */

# animation-play-state

控制动画播放状态:

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

# 2.4 动画简写语法

/* 完整写法 */
.element {
  animation-name: slidein;
  animation-duration: 2s;
  animation-timing-function: ease-in-out;
  animation-delay: 0s;
  animation-iteration-count: infinite;
  animation-direction: alternate;
  animation-fill-mode: both;
}

/* 简写形式 */
.element {
  animation: slidein 2s ease-in-out 0s infinite alternate both;
}

/* 多个动画 */
.element {
  animation: 
    slidein 2s ease-in-out infinite,
    fadein 1s ease;
}

# 2.5 实战示例

# 加载动画

<html>
  <div class="loader-container-c9e5f">
    <div class="spinner-c9e5f"></div>
    <div class="dots-c9e5f">
      <span></span>
      <span></span>
      <span></span>
    </div>
    <div class="pulse-c9e5f"></div>
  </div>
</html>

<style>
.loader-container-c9e5f {
  display: flex;
  gap: 50px;
  padding: 40px;
  justify-content: center;
  align-items: center;
}

/* 旋转加载器 */
.spinner-c9e5f {
  width: 50px;
  height: 50px;
  border: 4px solid #f3f3f3;
  border-top: 4px solid #3498db;
  border-radius: 50%;
  animation: spin-c9e5f 1s linear infinite;
}

@keyframes spin-c9e5f {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

/* 跳动点 */
.dots-c9e5f {
  display: flex;
  gap: 8px;
}

.dots-c9e5f span {
  width: 12px;
  height: 12px;
  background-color: #3498db;
  border-radius: 50%;
  animation: bounce-c9e5f 1.4s infinite ease-in-out both;
}

.dots-c9e5f span:nth-child(1) {
  animation-delay: -0.32s;
}

.dots-c9e5f span:nth-child(2) {
  animation-delay: -0.16s;
}

@keyframes bounce-c9e5f {
  0%, 80%, 100% {
    transform: scale(0);
    opacity: 0.5;
  }
  40% {
    transform: scale(1);
    opacity: 1;
  }
}

/* 脉冲效果 */
.pulse-c9e5f {
  width: 50px;
  height: 50px;
  background-color: #3498db;
  border-radius: 50%;
  animation: pulse-c9e5f 2s cubic-bezier(0.455, 0.03, 0.515, 0.955) infinite;
}

@keyframes pulse-c9e5f {
  0% {
    transform: scale(0.8);
    opacity: 1;
  }
  50% {
    transform: scale(1.2);
    opacity: 0.7;
  }
  100% {
    transform: scale(0.8);
    opacity: 1;
  }
}
</style>

# 悬浮动画

<html>
  <div class="float-container-d2f6a">
    <div class="float-box-d2f6a">
      <div class="icon-d2f6a">🚀</div>
      <h4>悬浮效果</h4>
    </div>
    <div class="bounce-box-d2f6a">
      <div class="icon-d2f6a">⚡</div>
      <h4>弹跳效果</h4>
    </div>
  </div>
</html>

<style>
.float-container-d2f6a {
  display: flex;
  gap: 40px;
  padding: 40px;
  justify-content: center;
}

.float-box-d2f6a,
.bounce-box-d2f6a {
  width: 150px;
  padding: 20px;
  text-align: center;
  background: white;
  border-radius: 10px;
  box-shadow: 0 4px 10px rgba(0,0,0,0.1);
}

.icon-d2f6a {
  font-size: 48px;
  margin-bottom: 10px;
}

.float-box-d2f6a h4,
.bounce-box-d2f6a h4 {
  margin: 0;
  color: #333;
  font-size: 14px;
}

/* 悬浮动画 */
.float-box-d2f6a {
  animation: float-d2f6a 3s ease-in-out infinite;
}

@keyframes float-d2f6a {
  0%, 100% {
    transform: translateY(0px);
  }
  50% {
    transform: translateY(-20px);
  }
}

/* 弹跳动画 */
.bounce-box-d2f6a {
  animation: bounce-box-d2f6a 2s ease infinite;
}

@keyframes bounce-box-d2f6a {
  0%, 20%, 50%, 80%, 100% {
    transform: translateY(0);
  }
  40% {
    transform: translateY(-20px);
  }
  60% {
    transform: translateY(-10px);
  }
}
</style>

# 文字动画

<html>
  <div class="text-container-e3g7b">
    <h2 class="typing-e3g7b">打字机效果...</h2>
    <h2 class="gradient-e3g7b">渐变流动文字</h2>
    <h2 class="neon-e3g7b">霓虹闪烁</h2>
  </div>
</html>

<style>
.text-container-e3g7b {
  padding: 40px;
  text-align: center;
  background: #1a1a2e;
  border-radius: 10px;
}

.text-container-e3g7b h2 {
  margin: 30px 0;
}

/* 打字机效果 */
.typing-e3g7b {
  color: #00ff00;
  font-family: 'Courier New', monospace;
  overflow: hidden;
  border-right: 3px solid #00ff00;
  white-space: nowrap;
  width: fit-content;
  margin: 30px auto;
  animation: 
    typing-e3g7b 3s steps(20, end) infinite,
    blink-e3g7b 0.75s step-end infinite;
}

@keyframes typing-e3g7b {
  0%, 90%, 100% {
    width: 0;
  }
  30%, 60% {
    width: 100%;
  }
}

@keyframes blink-e3g7b {
  50% {
    border-color: transparent;
  }
}

/* 渐变流动 */
.gradient-e3g7b {
  background: linear-gradient(
    90deg,
    #ff6b6b,
    #4ecdc4,
    #45b7d1,
    #f7b731,
    #ff6b6b
  );
  background-size: 200% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: gradient-flow-e3g7b 3s linear infinite;
}

@keyframes gradient-flow-e3g7b {
  0% {
    background-position: 0% 50%;
  }
  100% {
    background-position: 200% 50%;
  }
}

/* 霓虹闪烁 */
.neon-e3g7b {
  color: #fff;
  text-shadow: 
    0 0 10px #00ffff,
    0 0 20px #00ffff,
    0 0 30px #00ffff,
    0 0 40px #00ffff;
  animation: neon-e3g7b 1.5s ease-in-out infinite alternate;
}

@keyframes neon-e3g7b {
  from {
    text-shadow: 
      0 0 10px #00ffff,
      0 0 20px #00ffff,
      0 0 30px #00ffff,
      0 0 40px #00ffff,
      0 0 50px #00ffff;
  }
  to {
    text-shadow: 
      0 0 5px #00ffff,
      0 0 10px #00ffff,
      0 0 15px #00ffff,
      0 0 20px #00ffff;
  }
}
</style>

# 三、高级动画技巧

# 3.1 多步动画

创建包含多个阶段的复杂动画:

@keyframes complex-animation {
  0% {
    transform: translateX(0) rotate(0deg);
    background-color: #3498db;
  }
  25% {
    transform: translateX(100px) rotate(90deg);
    background-color: #e74c3c;
  }
  50% {
    transform: translateX(100px) translateY(100px) rotate(180deg);
    background-color: #2ecc71;
  }
  75% {
    transform: translateX(0) translateY(100px) rotate(270deg);
    background-color: #f39c12;
  }
  100% {
    transform: translateX(0) translateY(0) rotate(360deg);
    background-color: #3498db;
  }
}
<html>
  <div class="multi-step-container-f4h8c">
    <div class="moving-box-f4h8c"></div>
  </div>
</html>

<style>
.multi-step-container-f4h8c {
  height: 250px;
  padding: 20px;
  position: relative;
  background: #f5f5f5;
  border-radius: 10px;
}

.moving-box-f4h8c {
  width: 60px;
  height: 60px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 10px;
  position: absolute;
  animation: path-f4h8c 4s ease-in-out infinite;
}

@keyframes path-f4h8c {
  0% {
    left: 20px;
    top: 20px;
    transform: rotate(0deg);
    border-radius: 10px;
  }
  25% {
    left: calc(100% - 80px);
    top: 20px;
    transform: rotate(90deg);
    border-radius: 50%;
  }
  50% {
    left: calc(100% - 80px);
    top: calc(100% - 80px);
    transform: rotate(180deg);
    border-radius: 10px;
  }
  75% {
    left: 20px;
    top: calc(100% - 80px);
    transform: rotate(270deg);
    border-radius: 50%;
  }
  100% {
    left: 20px;
    top: 20px;
    transform: rotate(360deg);
    border-radius: 10px;
  }
}
</style>

# 3.2 动画组合

组合多个动画创建复杂效果:

<html>
  <div class="combined-container-g5i9d">
    <div class="ball-g5i9d"></div>
  </div>
</html>

<style>
.combined-container-g5i9d {
  height: 200px;
  padding: 20px;
  position: relative;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 10px;
  overflow: hidden;
}

.ball-g5i9d {
  width: 40px;
  height: 40px;
  background: white;
  border-radius: 50%;
  position: absolute;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
  box-shadow: 0 0 20px rgba(255,255,255,0.5);
  animation: 
    bounce-ball-g5i9d 2s ease-in-out infinite,
    swing-ball-g5i9d 3s ease-in-out infinite,
    glow-g5i9d 1s ease-in-out infinite alternate;
}

@keyframes bounce-ball-g5i9d {
  0%, 100% {
    top: 20px;
    animation-timing-function: ease-in;
  }
  50% {
    top: calc(100% - 60px);
    animation-timing-function: ease-out;
  }
}

@keyframes swing-ball-g5i9d {
  0%, 100% {
    left: 30%;
  }
  50% {
    left: 70%;
  }
}

@keyframes glow-g5i9d {
  from {
    box-shadow: 0 0 20px rgba(255,255,255,0.5);
  }
  to {
    box-shadow: 0 0 40px rgba(255,255,255,0.9);
  }
}
</style>

# 3.3 动画序列

使用 animation-delay 创建动画序列:

<html>
  <div class="sequence-container-h6j0e">
    <div class="bar-h6j0e" style="animation-delay: 0s"></div>
    <div class="bar-h6j0e" style="animation-delay: 0.1s"></div>
    <div class="bar-h6j0e" style="animation-delay: 0.2s"></div>
    <div class="bar-h6j0e" style="animation-delay: 0.3s"></div>
    <div class="bar-h6j0e" style="animation-delay: 0.4s"></div>
  </div>
</html>

<style>
.sequence-container-h6j0e {
  display: flex;
  gap: 10px;
  padding: 40px;
  justify-content: center;
  align-items: flex-end;
  height: 150px;
  background: #2c3e50;
  border-radius: 10px;
}

.bar-h6j0e {
  width: 30px;
  height: 100px;
  background: linear-gradient(180deg, #3498db, #2ecc71);
  border-radius: 5px;
  animation: wave-h6j0e 1.5s ease-in-out infinite;
}

@keyframes wave-h6j0e {
  0%, 100% {
    height: 30px;
  }
  50% {
    height: 100px;
  }
}
</style>

# 3.4 动画暂停与控制

通过JavaScript控制动画的播放状态:

<html>
  <div class="control-container-i7k1f">
    <div class="rotating-box-i7k1f" id="rotatingBox-i7k1f"></div>
    <div class="controls-i7k1f">
      <button onclick="document.getElementById('rotatingBox-i7k1f').style.animationPlayState='running'">
        播放
      </button>
      <button onclick="document.getElementById('rotatingBox-i7k1f').style.animationPlayState='paused'">
        暂停
      </button>
    </div>
  </div>
</html>

<style>
.control-container-i7k1f {
  padding: 40px;
  text-align: center;
}

.rotating-box-i7k1f {
  width: 80px;
  height: 80px;
  margin: 0 auto 30px;
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
  border-radius: 10px;
  animation: rotate-box-i7k1f 3s linear infinite;
}

@keyframes rotate-box-i7k1f {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

.controls-i7k1f {
  display: flex;
  gap: 10px;
  justify-content: center;
}

.controls-i7k1f button {
  padding: 10px 20px;
  background: #3498db;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 14px;
  transition: background-color 0.3s ease;
}

.controls-i7k1f button:hover {
  background: #2980b9;
}
</style>

# 四、性能优化

# 4.1 使用transform和opacity

为了获得最佳性能,优先使用 transform 和 opacity 属性,它们可以利用GPU加速:

/* ✅ 高性能:使用transform */
.element {
  transform: translateX(100px);
  transition: transform 0.3s;
}

/* ❌ 低性能:改变布局属性 */
.element {
  left: 100px;
  transition: left 0.3s;
}

推荐使用的属性:

  • transform(translate、rotate、scale)
  • opacity
  • filter(某些情况)

避免动画的属性:

  • width、height
  • top、left、right、bottom
  • margin、padding
  • border-width

# 4.2 will-change属性

使用 will-change 提示浏览器即将发生的变化,让浏览器提前优化:

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

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

/* 动画结束后移除 */
.element {
  transition: transform 0.3s;
}

.element.transitioning {
  will-change: transform;
}

注意

不要过度使用 will-change,它会消耗额外的内存。只在确实需要优化的元素上使用,并在动画结束后移除。

# 4.3 减少重绘和回流

触发回流(Reflow)的操作:

  • 改变几何属性:width、height、padding、margin、border
  • 改变字体:font-size、font-weight
  • 添加或删除DOM元素
  • 读取某些属性:offsetWidth、scrollTop等

触发重绘(Repaint)的操作:

  • 改变颜色:color、background-color
  • 改变可见性:visibility
  • 改变轮廓:outline

优化建议:

/* ✅ 使用transform替代位置属性 */
.element {
  transform: translateX(100px);
}

/* ❌ 避免频繁修改位置属性 */
.element {
  left: 100px;
}

/* ✅ 使用opacity替代visibility */
.element {
  opacity: 0;
  pointer-events: none;
}

/* ❌ visibility会触发重绘 */
.element {
  visibility: hidden;
}

# 4.4 使用requestAnimationFrame

在JavaScript中使用 requestAnimationFrame 而不是 setTimeout 或 setInterval:

// ✅ 推荐:使用requestAnimationFrame
function animate() {
  element.style.transform = `translateX(${position}px)`;
  position += 1;
  if (position < 300) {
    requestAnimationFrame(animate);
  }
}
requestAnimationFrame(animate);

// ❌ 不推荐:使用setTimeout
function animate() {
  element.style.transform = `translateX(${position}px)`;
  position += 1;
  if (position < 300) {
    setTimeout(animate, 16);
  }
}

# 4.5 性能测试

使用浏览器开发者工具的Performance面板监测动画性能:

// 在控制台测试FPS
let lastTime = performance.now();
let frames = 0;

function measureFPS() {
  frames++;
  const currentTime = performance.now();
  if (currentTime >= lastTime + 1000) {
    const fps = Math.round((frames * 1000) / (currentTime - lastTime));
    console.log(`FPS: ${fps}`);
    frames = 0;
    lastTime = currentTime;
  }
  requestAnimationFrame(measureFPS);
}

measureFPS();

性能目标

  • 保持60FPS(每帧约16.67ms)
  • 避免长时间的布局计算(>50ms)
  • 减少合成层(composite layers)的数量

# 五、最佳实践

# 5.1 动画设计原则

# 有目的的动画

动画应该服务于用户体验,而不是仅仅为了"好看":

  • 反馈:确认用户操作(按钮点击、表单提交)
  • 引导:吸引注意力到重要元素
  • 关系:展示元素之间的关系(父子、顺序)
  • 连续性:保持界面变化的连贯性

# 动画时长建议

根据动画类型选择合适的时长:

动画类型 建议时长 说明
微交互 100-300ms 按钮悬停、小元素移动
中等动画 300-500ms 卡片展开、菜单显示
大型动画 500-800ms 页面过渡、复杂变化
背景动画 1s+ 装饰性、循环动画
/* 微交互 */
.button {
  transition: background-color 0.2s;
}

/* 中等动画 */
.modal {
  animation: fadeIn 0.4s ease;
}

/* 大型动画 */
.page-transition {
  animation: slideIn 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

# 5.2 缓动函数选择

不同场景使用不同的缓动函数:

/* 入场动画:ease-out(快速开始,慢速结束) */
.fade-in {
  animation: fadeIn 0.4s ease-out;
}

/* 出场动画:ease-in(慢速开始,快速结束) */
.fade-out {
  animation: fadeOut 0.3s ease-in;
}

/* 用户触发:ease-in-out(平滑) */
.modal {
  transition: transform 0.4s ease-in-out;
}

/* 物理效果:cubic-bezier */
.bounce {
  animation: bounce 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
<html>
  <div class="easing-demo-j8l2g">
    <div class="ball-demo-j8l2g ease-j8l2g">ease</div>
    <div class="ball-demo-j8l2g linear-j8l2g">linear</div>
    <div class="ball-demo-j8l2g ease-in-j8l2g">ease-in</div>
    <div class="ball-demo-j8l2g ease-out-j8l2g">ease-out</div>
    <div class="ball-demo-j8l2g ease-in-out-j8l2g">ease-in-out</div>
  </div>
</html>

<style>
.easing-demo-j8l2g {
  padding: 20px;
  background: #f5f5f5;
  border-radius: 10px;
  position: relative;
  overflow: hidden;
}

.ball-demo-j8l2g {
  width: 80px;
  height: 40px;
  margin: 10px 0;
  background: #3498db;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 20px;
  font-size: 12px;
  animation-duration: 2s;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}

.ease-j8l2g {
  animation-name: move-j8l2g;
  animation-timing-function: ease;
}

.linear-j8l2g {
  animation-name: move-j8l2g;
  animation-timing-function: linear;
  background: #e74c3c;
}

.ease-in-j8l2g {
  animation-name: move-j8l2g;
  animation-timing-function: ease-in;
  background: #2ecc71;
}

.ease-out-j8l2g {
  animation-name: move-j8l2g;
  animation-timing-function: ease-out;
  background: #f39c12;
}

.ease-in-out-j8l2g {
  animation-name: move-j8l2g;
  animation-timing-function: ease-in-out;
  background: #9b59b6;
}

@keyframes move-j8l2g {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(400px);
  }
}
</style>

# 5.3 响应式动画

根据设备性能和用户偏好调整动画:

/* 根据用户偏好禁用动画 */
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* 在低性能设备上简化动画 */
@media (prefers-reduced-motion: no-preference) and (max-width: 768px) {
  .complex-animation {
    animation: simple-fade 0.3s ease;
  }
}

@media (min-width: 769px) {
  .complex-animation {
    animation: complex-transform 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94);
  }
}

# 5.4 可访问性考虑

确保动画不会影响可访问性:

/* 尊重用户的运动偏好 */
@media (prefers-reduced-motion: reduce) {
  .animated {
    animation: none;
    transition: none;
  }
}

/* 避免闪烁频率过高 */
.blink {
  animation: blink 1s infinite; /* 频率 < 3Hz */
}

/* 确保动画不影响内容可读性 */
.text-animation {
  animation: slide 2s ease;
  /* 在动画过程中保持文字可读 */
}

# 5.5 调试技巧

使用CSS和浏览器工具调试动画:

/* 放慢动画速度便于调试 */
* {
  animation-duration: 10s !important;
  transition-duration: 2s !important;
}

/* 显示动画边界 */
.animated {
  outline: 2px solid red;
}

/* 暂停所有动画 */
* {
  animation-play-state: paused !important;
}

在浏览器开发者工具中:

  1. Chrome DevTools:Elements → Animations 面板
  2. Firefox DevTools:Inspector → Animations
  3. 可以放慢速度、逐帧查看、重放动画

# 六、实用动画库推荐

# 6.1 常用CSS动画库

虽然我们可以手写所有动画,但使用成熟的动画库可以提高开发效率:

# Animate.css

<!-- 引入Animate.css -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/>

<!-- 使用动画 -->
<div class="animate__animated animate__bounce">
  弹跳效果
</div>

# Hover.css

专注于悬停效果的CSS库:

/* 模仿Hover.css的效果 */
.hvr-grow {
  transition: transform 0.3s;
}

.hvr-grow:hover {
  transform: scale(1.1);
}

# 6.2 JavaScript动画库

对于复杂的动画序列,可以考虑使用JavaScript库:

  • GSAP:功能强大的动画库
  • Anime.js:轻量级动画库
  • Framer Motion:React动画库
  • Motion One:现代Web动画库

# 七、浏览器兼容性

# 7.1 Transition兼容性

transition 属性在现代浏览器中有良好支持:

浏览器 支持版本
Chrome 26+
Firefox 16+
Safari 9+
Edge 12+
IE 10+

# 7.2 Animation兼容性

animation 属性同样有广泛支持:

浏览器 支持版本
Chrome 43+
Firefox 16+
Safari 9+
Edge 12+
IE 10+

# 7.3 前缀处理

对于旧版浏览器,可能需要添加前缀:

/* 手动添加前缀 */
.element {
  -webkit-transition: all 0.3s;
  -moz-transition: all 0.3s;
  -o-transition: all 0.3s;
  transition: all 0.3s;
}

/* 使用Autoprefixer自动添加 */
.element {
  transition: all 0.3s;
}

建议使用 Autoprefixer 等工具自动处理浏览器前缀。

# 八、总结

CSS动画与过渡是现代Web开发的重要组成部分。通过本文,我们学习了:

  1. 过渡(Transition):

    • 适用于简单的状态变化
    • 需要触发条件(如:hover)
    • 使用 transition 属性控制
  2. 动画(Animation):

    • 适用于复杂的动画序列
    • 使用 @keyframes 定义关键帧
    • 可以自动播放、循环、反向等
  3. 性能优化:

    • 优先使用 transform 和 opacity
    • 合理使用 will-change
    • 避免触发回流和重绘
    • 保持60FPS的流畅度
  4. 最佳实践:

    • 动画要有明确目的
    • 选择合适的时长和缓动函数
    • 考虑可访问性和用户偏好
    • 响应式动画设计

记住:好的动画是为了增强用户体验,而不是炫技。合理使用动画,让你的网站更加生动、直观、易用。

祝你变得更强!

编辑 (opens new window)
#CSS
上次更新: 2025/11/20
CSS布局-响应式设计实践
CSS进阶-渐变与阴影效果

← CSS布局-响应式设计实践 CSS进阶-渐变与阴影效果→

最近更新
01
AI编程时代的一些心得
09-11
02
Claude Code与Codex的协同工作
09-01
03
Claude Code 最佳实践(个人版)
08-01
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式