中级前端进阶 第十六步) 深入CSS 动画,附源码,可视化演示等

  🤙《中级前端进阶方向指南》

《中级前端进阶方向 第一步)JavaScript 微任务 / 宏任务机制》

《中级前端进阶方向 第二步)深入事件循环(Event Loop)》

《中级前端进阶方向 第三步)深入javascript原型链,附学习代码》

《中级前端进阶方向 第四步)深入javascript 闭包、this 、作用域提升,附学习案例源码》

《​​中级前端进阶方向 第五步)深入 javascript高级特性Proxy,附学习案例源码》

《​​中级前端进阶方向 第六步)深入 javascript 高级特性Generator生成器,附案例源码》
《​​中级前端进阶方向 第七步)深入 javascript 高级特性Async/Await,附源码》

《​​中级前端进阶方向 第八步)深入 javascript 高级特性 Promise,附案例源码》

《​​中级前端进阶方向 第九步)深入 javascript 高级特性 模块化 与 装饰器,附案例源码》

《中级前端进阶方向 第十步)深入 浏览器性能优化(渲染机制、内存泄漏..),附案例源码》


--🖍《CSS》-----------------------------------------------------------------------------------------

《中级前端进阶 第十一步) 深入CSS3 新特性,附源码,可视化演示等》

《中级前端进阶 第十二步) 深入CSS3 Flex布局,附源码,可视化演示等》

《中级前端进阶 第十三步) 深入CSS3 Grid布局,附源码,可视化演示等》

《中级前端进阶 第十四步) 深入CSS3 响应式设计,附源码,可视化演示等》

《中级前端进阶 第十五步) 深入CSS 动态主题切换,附源码,可视化演示等》


--🖍《延伸扩展》-----------------------------------------------------------------------------------------
《中级前端进阶方向 延伸扩展一)javascript 私有状态/工厂函数/回调中保持局部状态/防抖与节流/IIFE 捕获》

《中级前端进阶方向 延伸扩展二) Promise API 概览,可视化演示页面》

《中级前端进阶 延伸扩展三) CSS单位大全,附源码,可视化演示等》


1 原理与分类(Transition vs Animation vs Web Animations API)

  • transition(过渡):在两个状态之间平滑过渡,适合交互触发(hover、focus、class 切换)。语义简单,适合微交互。

  • @keyframes animation(关键帧动画):可定义多段关键帧(0%/50%/100% 等),支持无限循环、反向、分段控制,适合复杂或持续动画(loading、轮播、精灵等)。

  • Web Animations API(WAAPI):浏览器原生 JS API,提供对关键帧动画的精细 JS 控制(播放、暂停、速率、时间线、承诺式结束通知等),比仅用 CSS 在 JS 控制上更强大且性能友好。


2 关键属性(动画配置项)

常用 CSS 属性(animation- / transition-)Transition(过渡)主要属性:

transition-property: all | opacity | transform | ...;
transition-duration: 200ms;
transition-timing-function: ease | linear | cubic-bezier(...);
transition-delay: 0s;

Animation(关键帧)主要属性:

/* 基本 */
animation-name: slideIn;
animation-duration: 1s;
animation-timing-function: ease-in-out;
animation-delay: 0s;
animation-iteration-count: 1 | infinite;
animation-direction: normal | reverse | alternate | alternate-reverse;
animation-fill-mode: none | forwards | backwards | both;
animation-play-state: running | paused;

/* 缩写 */
animation: slideIn 1s ease-in-out 0s 1 normal forwards;

事件(DOM):

  • animationstartanimationiterationanimationend(可监听 JS 中做链式动作或清理)


3 @keyframes 语法与示例

@keyframes bounce {
  0%   { transform: translateY(0); }
  50%  { transform: translateY(-16px); }
  100% { transform: translateY(0); }
}

/* 使用 */
.ball {
  animation: bounce 600ms cubic-bezier(.2,.8,.2,1) infinite;
}
  • % 或关键字 from(0%)/to(100%)都可。

  • 支持多个动画并列,用逗号分隔 animation: a 1s, b 2s;


4 时间函数(timing-function)

  • 预定义:linear, ease, ease-in, ease-out, ease-in-out

  • 自定义:cubic-bezier(x1, y1, x2, y2) 用于精确控制速度曲线。

  • 帧式/步进:steps(n, start|end) 用于逐帧(精灵图或帧动画)。例如走马灯/帧动画 steps(10)

  • 注意:animation-timing-function 可配合关键帧局部覆盖速度曲线。

示例(steps 用来做 sprite):

.sprite {
  background-image: url(sprite.png);
  animation: play 1s steps(10) infinite;
}
@keyframes play {
  from { background-position: 0 0; }
  to   { background-position: -1000px 0; } /* 10 帧宽总和 */
}

5 常见设计模式(实战动效)

  • 淡入/淡出(fade)opacity + transform: translateY(...)(避免只用 opacity? opacity 就很好,transform 加位移更自然)。

  • 滑入(slide)transform: translateX/Y,在元素脱离文档流时使用。

  • 缩放弹性(scale bounce)transform: scale() + cubic-bezier 或 关键帧。

  • 旋转加载器(spinner)transform: rotate() + animation-iteration-count: infinite

  • 精灵动画(sprite):使用 steps()background-position

  • 抖动/提示(shake):小幅度 translateX 来吸引注意。

  • 文本打字机(typewriter):结合 steps()ch 宽度或 clip-path

  • 页面加载过渡opacity + transform + animation-fill-mode: forwards 控制结束状态。


6 性能优化(最重要的部分)

动画的性能关键在于尽量只触发合成(composite)层,避免触发布局(reflow)或重绘(repaint)。

  • 推荐只动画化 transformopacity —— 它们通常只会触发合成层(GPU),最流畅。

  • 尽量避免动画化会触发 layout 的属性width, height, top, left, margin, padding, border-width,这些会导致 reflow → expensive。

  • 避免频繁改变 background-image / filter / box-shadow(尤其 blur):这些通常触发重绘或昂贵的光栅化。

  • 使用 will-change 谨慎will-change: transform 可提前告知浏览器创建独立图层,但会占用显存。只在动画前短期设置,动画结束后移除。

  • 合成层数量要平衡:每个独立图层占用内存,图层过多也会导致内存开销/GC。

  • 使用 3D 提示慎用translateZ(0) 强制创建图层,但不是万能解;更好通过 will-change 并在必要时移除。

  • 避免同步读写样式(layout thrashing):在 JS 中修改样式后立即读取会触发强制回流(e.g., read offsetHeight after changing style)。批量写入、再读或使用 requestAnimationFrame。

性能检查工具:

  • Chrome DevTools: Performance 面板(录制帧率/主线程/合成层),Rendering -> Paint flashing、Layer borders、Show FPS meter 等。


7 可访问性(prefers-reduced-motion)

  • 遵守用户设置:@media (prefers-reduced-motion: reduce)

@media (prefers-reduced-motion: reduce) {
  .animated { animation: none !important; transition: none !important; }
}
  • 动画不应影响信息获取(例如重要的内容不能仅由动画提示),也要避免诱发晕动/癫痫触发的快速闪烁。


8 JS 与 Web Animations API(精细控制)

CSS 控制场景: 简单、声明式、由 class 切换触发。
JS 控制场景: 需要暂停/继续、设置速率、读取当前时间、复杂同步或交互驱动时用 Web Animations API。例:用 WAAPI 创建并控制动画

const el = document.querySelector('.box');
const anim = el.animate([
  { transform: 'translateY(0)', opacity: 1 },
  { transform: 'translateY(-20px)', opacity: 0.8 },
  { transform: 'translateY(0)', opacity: 1 }
], {
  duration: 800,
  iterations: Infinity,
  easing: 'ease-in-out'
});

// 控制
anim.pause();
anim.play();
anim.updatePlaybackRate(2); // 提速
anim.cancel();
anim.finish();

WAAPI 的优势:返回 Animation 对象,可通过 Promise (finished) 等方式链式控制,性能好(浏览器优化)。


9 进阶技巧(stagger、sequence、sprite、3D)

  • stagger(错位延迟):用 animation-delaynth-child() 或 CSS 变量计算:

.item { animation: pop .4s cubic-bezier(...) both; animation-delay: calc(var(--i) * 80ms); }
  • 序列化(chain):监听 animationend 或使用 WAAPI 的 finished promise,开始下一个动画。

  • sprite 帧动画:用 steps() + background-position 逐帧显示。

  • 3D flip:使用 transform-style: preserve-3dbackface-visibility: hidden 做卡片翻转,注意开启硬件加速与层管理。


10 常见实现示例(可直接运行)

简单 Fade + Slide(transition):

<button class="btn">Hover me</button>
<style>
.btn{
  display:inline-block; padding:12px 20px; background:#0b79ff; color:white; border-radius:6px;
  transform: translateY(0);
  transition: transform .2s cubic-bezier(.2,.9,.2,1), box-shadow .2s;
}
.btn:hover { transform: translateY(-4px); box-shadow: 0 8px 20px rgba(11,121,255,.18); }
</style>

关键帧 Spinner:

<div class="spinner"></div>
<style>
.spinner{
  width:40px;height:40px;border:4px solid #eee;border-top-color:#0b79ff;border-radius:50%;
  animation: spin 800ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
</style>

列错位 Stagger(利用 CSS 变量):

<ul class="list">
  <li style="--i:0">A</li><li style="--i:1">B</li><li style="--i:2">C</li>
</ul>
<style>
.list li{ opacity:0; transform: translateY(8px);
  animation: pop .36s cubic-bezier(.2,.8,.2,1) both;
  animation-delay: calc(var(--i) * 80ms);
}
@keyframes pop { to { opacity:1; transform:none; } }
</style>

帧动画(sprite):

.sprite{
  width:100px;height:100px;background:url(sprite.png) 0 0 no-repeat;
  animation: play 1s steps(10) infinite;
}
@keyframes play { from { background-position: 0 0; } to { background-position: -1000px 0; } }

11 调试技巧

  • DevTools → Performance:录制并查看帧时间线、主线程阻塞、层合成。

  • DevTools → Rendering:打开 “Paint flashing” 看哪些区域在重绘,或 “Layer borders” 查看图层。

  • 临时在元素上加 will-change: transform 观察效果(记得移除)。

  • animation-play-state: paused 暂停并查看最终样式。


12 常见误区与最佳实践速查

  • 误区transition 可以替代所有动画 —— 复杂多段动画仍需 @keyframes 或 WAAPI。

  • 避免:动画大量使用 box-shadow 模糊、filter: blur()background-image 位移(高开销)。

  • 优先transform + opacity;使用 will-change 精确提示;释放图层(不要长期占用);采用 prefers-reduced-motion

  • 控制:尽量用 CSS 变量控制时长/延迟,便于主题/调试修改。

  • 可访问性:不要用动画代替必要信息;允许用户关闭动画(遵守 prefers-reduced-motion)。


13 小结 

  • transition 做状态过渡(hover/focus);用 @keyframes animation 做持续或复杂动画;用 WAAPI 做精细 JS 控制。

  • 优先动画化 transform / opacity,避免触发布局/重绘。

  • 使用 will-change 谨慎并短期生效;不要过度图层化。

  • 加入 @media (prefers-reduced-motion: reduce) 支持无动画偏好。

  • 用动画事件或 WAAPI 的 Promise 实现链式/序列化控制。

  • 测试性能(DevTools Performance/Rendering)并优化热点。


14 可视化源代码

演示页面包含以下核心功能:

  1. 六种动画类型

    • 淡入淡出 (Fade)

    • 滑动 (Slide)

    • 缩放 (Scale)

    • 旋转 (Rotate)

    • 弹跳 (Bounce)

    • 颜色变化 (Color Change)

  2. 交互式控制

    • 每个动画都有可调整的参数(持续时间、延迟、方向等)

    • 实时更新动画效果和代码示例

    • 播放按钮可以重新触发动画

  3. 分类筛选

    • 可以通过顶部按钮筛选显示特定类型的动画

  4. 实时代码生成

    • 每个动画都显示对应的CSS代码

    • 代码会根据参数调整实时更新

  5. 响应式设计

    • 页面适配各种屏幕尺寸

      <!DOCTYPE html>
      <html lang="zh-CN">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>CSS动画可视化演示</title>
          <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
          <style>
              :root {
                  --primary: #4361ee;
                  --secondary: #3a0ca3;
                  --accent: #f72585;
                  --light: #f8f9fa;
                  --dark: #212529;
                  --success: #4cc9f0;
                  --warning: #ffbe0b;
                  --gray: #adb5bd;
                  --transition: all 0.3s ease;
              }
      
              * {
                  box-sizing: border-box;
                  margin: 0;
                  padding: 0;
              }
      
              body {
                  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
                  line-height: 1.6;
                  color: var(--dark);
                  background: linear-gradient(135deg, #f5f7fa 0%, #e2e8f0 100%);
                  min-height: 100vh;
                  padding: 20px;
              }
      
              .container {
                  max-width: 1200px;
                  margin: 0 auto;
              }
      
              header {
                  text-align: center;
                  margin-bottom: 40px;
                  padding: 30px;
                  background: rgba(255, 255, 255, 0.95);
                  border-radius: 15px;
                  box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
              }
      
              h1 {
                  color: var(--primary);
                  margin-bottom: 15px;
                  font-size: 2.8rem;
              }
      
              .description {
                  color: var(--gray);
                  max-width: 800px;
                  margin: 0 auto;
                  font-size: 1.1rem;
              }
      
              .animation-types {
                  display: grid;
                  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
                  gap: 25px;
                  margin-bottom: 40px;
              }
      
              .animation-card {
                  background: white;
                  border-radius: 15px;
                  overflow: hidden;
                  box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
                  transition: var(--transition);
              }
      
              .animation-card:hover {
                  transform: translateY(-5px);
                  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
              }
      
              .card-header {
                  padding: 20px;
                  background: var(--primary);
                  color: white;
                  font-size: 1.4rem;
                  font-weight: bold;
                  display: flex;
                  align-items: center;
                  gap: 10px;
              }
      
              .card-content {
                  padding: 20px;
              }
      
              .demo-area {
                  height: 150px;
                  display: flex;
                  align-items: center;
                  justify-content: center;
                  margin-bottom: 20px;
                  background: var(--light);
                  border-radius: 10px;
                  position: relative;
              }
      
              .animated-element {
                  width: 70px;
                  height: 70px;
                  background: var(--primary);
                  border-radius: 8px;
                  display: flex;
                  align-items: center;
                  justify-content: center;
                  color: white;
                  font-weight: bold;
                  position: relative;
              }
      
              /* 动画定义 */
              @keyframes fade {
                  0% { opacity: 0; }
                  100% { opacity: 1; }
              }
      
              @keyframes slide {
                  0% { transform: translateX(-100px); }
                  100% { transform: translateX(0); }
              }
      
              @keyframes scale {
                  0% { transform: scale(0.5); }
                  100% { transform: scale(1); }
              }
      
              @keyframes rotate {
                  0% { transform: rotate(0deg); }
                  100% { transform: rotate(360deg); }
              }
      
              @keyframes bounce {
                  0%, 100% { transform: translateY(0); }
                  50% { transform: translateY(-30px); }
              }
      
              @keyframes colorChange {
                  0% { background-color: var(--primary); }
                  50% { background-color: var(--accent); }
                  100% { background-color: var(--primary); }
              }
      
              .controls {
                  display: grid;
                  grid-template-columns: 1fr 1fr;
                  gap: 15px;
                  margin-bottom: 20px;
              }
      
              .control-group {
                  margin-bottom: 15px;
              }
      
              label {
                  display: block;
                  margin-bottom: 8px;
                  font-weight: 500;
                  color: var(--dark);
              }
      
              input[type="range"] {
                  width: 100%;
              }
      
              .value-display {
                  display: inline-block;
                  min-width: 40px;
                  text-align: right;
                  font-weight: 600;
                  color: var(--primary);
              }
      
              .play-btn {
                  width: 100%;
                  padding: 12px;
                  background: var(--primary);
                  color: white;
                  border: none;
                  border-radius: 8px;
                  cursor: pointer;
                  font-weight: bold;
                  transition: var(--transition);
                  display: flex;
                  align-items: center;
                  justify-content: center;
                  gap: 8px;
              }
      
              .play-btn:hover {
                  background: var(--secondary);
              }
      
              .code-example {
                  background: #2d3748;
                  color: #e2e8f0;
                  padding: 15px;
                  border-radius: 10px;
                  font-family: 'Fira Code', monospace;
                  overflow-x: auto;
                  margin-top: 20px;
                  font-size: 0.85rem;
              }
      
              .keyword {
                  color: #63b3ed;
              }
      
              .value {
                  color: #68d391;
              }
      
              .comment {
                  color: #a0aec0;
              }
      
              footer {
                  text-align: center;
                  margin-top: 50px;
                  padding: 20px;
                  color: var(--gray);
              }
      
              /* 响应式设计 */
              @media (max-width: 768px) {
                  .animation-types {
                      grid-template-columns: 1fr;
                  }
                  
                  .controls {
                      grid-template-columns: 1fr;
                  }
              }
      
              .animation-controls {
                  display: flex;
                  gap: 15px;
                  margin-top: 30px;
                  flex-wrap: wrap;
                  justify-content: center;
              }
      
              .anim-control-btn {
                  padding: 10px 20px;
                  background: var(--light);
                  border: 2px solid var(--primary);
                  border-radius: 8px;
                  cursor: pointer;
                  font-weight: 500;
                  transition: var(--transition);
              }
      
              .anim-control-btn.active {
                  background: var(--primary);
                  color: white;
              }
      
              .global-controls {
                  background: white;
                  padding: 25px;
                  border-radius: 15px;
                  box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
                  margin-bottom: 30px;
              }
      
              .global-controls h2 {
                  color: var(--primary);
                  margin-bottom: 20px;
                  text-align: center;
              }
          </style>
      </head>
      <body>
          <div class="container">
              <header>
                  <h1>CSS动画可视化演示</h1>
                  <p class="description">探索CSS动画的各种类型和效果。每个演示都包含可调整的参数和实时生成的CSS代码。</p>
              </header>
      
              <div class="global-controls">
                  <h2>全局控制</h2>
                  <div class="animation-controls">
                      <button class="anim-control-btn active" data-animation="all">所有动画</button>
                      <button class="anim-control-btn" data-animation="fade">淡入淡出</button>
                      <button class="anim-control-btn" data-animation="slide">滑动</button>
                      <button class="anim-control-btn" data-animation="scale">缩放</button>
                      <button class="anim-control-btn" data-animation="rotate">旋转</button>
                      <button class="anim-control-btn" data-animation="bounce">弹跳</button>
                      <button class="anim-control-btn" data-animation="color">颜色变化</button>
                  </div>
              </div>
      
              <div class="animation-types">
                  <!-- 淡入淡出动画 -->
                  <div class="animation-card" data-animation="fade">
                      <div class="card-header">
                          <i class="fas fa-eye"></i> 淡入淡出
                      </div>
                      <div class="card-content">
                          <div class="demo-area">
                              <div class="animated-element" id="fadeElement">Fade</div>
                          </div>
                          
                          <div class="controls">
                              <div class="control-group">
                                  <label>持续时间: <span class="value-display" id="fadeDurationValue">2</span>s</label>
                                  <input type="range" id="fadeDuration" min="0.5" max="5" step="0.5" value="2">
                              </div>
                              
                              <div class="control-group">
                                  <label>延迟: <span class="value-display" id="fadeDelayValue">0</span>s</label>
                                  <input type="range" id="fadeDelay" min="0" max="3" step="0.5" value="0">
                              </div>
                          </div>
                          
                          <button class="play-btn" data-target="fade">
                              <i class="fas fa-play"></i> 播放动画
                          </button>
                          
                          <div class="code-example">
                              <span class="comment">/* 淡入动画 */</span><br>
                              <span class="keyword">@keyframes</span> <span class="value">fade</span> {<br>
                              &nbsp;&nbsp;<span class="value">0%</span> { <span class="keyword">opacity</span>: <span class="value">0</span>; }<br>
                              &nbsp;&nbsp;<span class="value">100%</span> { <span class="keyword">opacity</span>: <span class="value">1</span>; }<br>
                              }<br><br>
                              
                              <span class="comment">/* 应用动画 */</span><br>
                              <span class="value">#fadeElement</span> {<br>
                              &nbsp;&nbsp;<span class="keyword">animation</span>: <span class="value">fade</span> <span id="fadeDurationCode">2</span>s <span class="value">ease-in-out</span> <span id="fadeDelayCode">0</span>s <span class="value">infinite</span>;<br>
                              }
                          </div>
                      </div>
                  </div>
      
                  <!-- 滑动动画 -->
                  <div class="animation-card" data-animation="slide">
                      <div class="card-header">
                          <i class="fas fa-arrows-alt-h"></i> 滑动
                      </div>
                      <div class="card-content">
                          <div class="demo-area">
                              <div class="animated-element" id="slideElement">Slide</div>
                          </div>
                          
                          <div class="controls">
                              <div class="control-group">
                                  <label>持续时间: <span class="value-display" id="slideDurationValue">2</span>s</label>
                                  <input type="range" id="slideDuration" min="0.5" max="5" step="0.5" value="2">
                              </div>
                              
                              <div class="control-group">
                                  <label>方向: 
                                      <select id="slideDirection">
                                          <option value="X">水平</option>
                                          <option value="Y">垂直</option>
                                      </select>
                                  </label>
                              </div>
                          </div>
                          
                          <button class="play-btn" data-target="slide">
                              <i class="fas fa-play"></i> 播放动画
                          </button>
                          
                          <div class="code-example">
                              <span class="comment">/* 滑动动画 */</span><br>
                              <span class="keyword">@keyframes</span> <span class="value">slide</span> {<br>
                              &nbsp;&nbsp;<span class="value">0%</span> { <span class="keyword">transform</span>: <span class="value">translateX(-100px)</span>; }<br>
                              &nbsp;&nbsp;<span class="value">100%</span> { <span class="keyword">transform</span>: <span class="value">translateX(0)</span>; }<br>
                              }<br><br>
                              
                              <span class="comment">/* 应用动画 */</span><br>
                              <span class="value">#slideElement</span> {<br>
                              &nbsp;&nbsp;<span class="keyword">animation</span>: <span class="value">slide</span> <span id="slideDurationCode">2</span>s <span class="value">ease-out</span>;<br>
                              }
                          </div>
                      </div>
                  </div>
      
                  <!-- 缩放动画 -->
                  <div class="animation-card" data-animation="scale">
                      <div class="card-header">
                          <i class="fas fa-expand-arrows-alt"></i> 缩放
                      </div>
                      <div class="card-content">
                          <div class="demo-area">
                              <div class="animated-element" id="scaleElement">Scale</div>
                          </div>
                          
                          <div class="controls">
                              <div class="control-group">
                                  <label>持续时间: <span class="value-display" id="scaleDurationValue">1.5</span>s</label>
                                  <input type="range" id="scaleDuration" min="0.5" max="5" step="0.5" value="1.5">
                              </div>
                              
                              <div class="control-group">
                                  <label>缩放程度: <span class="value-display" id="scaleAmountValue">0.5</span></label>
                                  <input type="range" id="scaleAmount" min="0.1" max="0.9" step="0.1" value="0.5">
                              </div>
                          </div>
                          
                          <button class="play-btn" data-target="scale">
                              <i class="fas fa-play"></i> 播放动画
                          </button>
                          
                          <div class="code-example">
                              <span class="comment">/* 缩放动画 */</span><br>
                              <span class="keyword">@keyframes</span> <span class="value">scale</span> {<br>
                              &nbsp;&nbsp;<span class="value">0%</span> { <span class="keyword">transform</span>: <span class="value">scale(<span id="scaleAmountCode">0.5</span>)</span>; }<br>
                              &nbsp;&nbsp;<span class="value">100%</span> { <span class="keyword">transform</span>: <span class="value">scale(1)</span>; }<br>
                              }<br><br>
                              
                              <span class="comment">/* 应用动画 */</span><br>
                              <span class="value">#scaleElement</span> {<br>
                              &nbsp;&nbsp;<span class="keyword">animation</span>: <span class="value">scale</span> <span id="scaleDurationCode">1.5</span>s <span class="value">ease-out</span>;<br>
                              }
                          </div>
                      </div>
                  </div>
      
                  <!-- 旋转动画 -->
                  <div class="animation-card" data-animation="rotate">
                      <div class="card-header">
                          <i class="fas fa-sync"></i> 旋转
                      </div>
                      <div class="card-content">
                          <div class="demo-area">
                              <div class="animated-element" id="rotateElement">Rotate</div>
                          </div>
                          
                          <div class="controls">
                              <div class="control-group">
                                  <label>持续时间: <span class="value-display" id="rotateDurationValue">3</span>s</label>
                                  <input type="range" id="rotateDuration" min="1" max="10" step="1" value="3">
                              </div>
                              
                              <div class="control-group">
                                  <label>旋转角度: 
                                      <select id="rotateAmount">
                                          <option value="360">360° (完整一圈)</option>
                                          <option value="180">180°</option>
                                          <option value="90">90°</option>
                                          <option value="-360">-360° (反向)</option>
                                      </select>
                                  </label>
                              </div>
                          </div>
                          
                          <button class="play-btn" data-target="rotate">
                              <i class="fas fa-play"></i> 播放动画
                          </button>
                          
                          <div class="code-example">
                              <span class="comment">/* 旋转动画 */</span><br>
                              <span class="keyword">@keyframes</span> <span class="value">rotate</span> {<br>
                              &nbsp;&nbsp;<span class="value">0%</span> { <span class="keyword">transform</span>: <span class="value">rotate(0deg)</span>; }<br>
                              &nbsp;&nbsp;<span class="value">100%</span> { <span class="keyword">transform</span>: <span class="value">rotate(<span id="rotateAmountCode">360</span>deg)</span>; }<br>
                              }<br><br>
                              
                              <span class="comment">/* 应用动画 */</span><br>
                              <span class="value">#rotateElement</span> {<br>
                              &nbsp;&nbsp;<span class="keyword">animation</span>: <span class="value">rotate</span> <span id="rotateDurationCode">3</span>s <span class="value">linear</span> <span class="value">infinite</span>;<br>
                              }
                          </div>
                      </div>
                  </div>
      
                  <!-- 弹跳动画 -->
                  <div class="animation-card" data-animation="bounce">
                      <div class="card-header">
                          <i class="fas fa-basketball-ball"></i> 弹跳
                      </div>
                      <div class="card-content">
                          <div class="demo-area">
                              <div class="animated-element" id="bounceElement">Bounce</div>
                          </div>
                          
                          <div class="controls">
                              <div class="control-group">
                                  <label>持续时间: <span class="value-display" id="bounceDurationValue">1</span>s</label>
                                  <input type="range" id="bounceDuration" min="0.5" max="3" step="0.5" value="1">
                              </div>
                              
                              <div class="control-group">
                                  <label>弹跳高度: <span class="value-display" id="bounceHeightValue">30</span>px</label>
                                  <input type="range" id="bounceHeight" min="10" max="100" step="10" value="30">
                              </div>
                          </div>
                          
                          <button class="play-btn" data-target="bounce">
                              <i class="fas fa-play"></i> 播放动画
                          </button>
                          
                          <div class="code-example">
                              <span class="comment">/* 弹跳动画 */</span><br>
                              <span class="keyword">@keyframes</span> <span class="value">bounce</span> {<br>
                              &nbsp;&nbsp;<span class="value">0%, 100%</span> { <span class="keyword">transform</span>: <span class="value">translateY(0)</span>; }<br>
                              &nbsp;&nbsp;<span class="value">50%</span> { <span class="keyword">transform</span>: <span class="value">translateY(-<span id="bounceHeightCode">30</span>px)</span>; }<br>
                              }<br><br>
                              
                              <span class="comment">/* 应用动画 */</span><br>
                              <span class="value">#bounceElement</span> {<br>
                              &nbsp;&nbsp;<span class="keyword">animation</span>: <span class="value">bounce</span> <span id="bounceDurationCode">1</span>s <span class="value">ease-in-out</span> <span class="value">infinite</span>;<br>
                              }
                          </div>
                      </div>
                  </div>
      
                  <!-- 颜色变化动画 -->
                  <div class="animation-card" data-animation="color">
                      <div class="card-header">
                          <i class="fas fa-palette"></i> 颜色变化
                      </div>
                      <div class="card-content">
                          <div class="demo-area">
                              <div class="animated-element" id="colorElement">Color</div>
                          </div>
                          
                          <div class="controls">
                              <div class="control-group">
                                  <label>持续时间: <span class="value-display" id="colorDurationValue">3</span>s</label>
                                  <input type="range" id="colorDuration" min="1" max="5" step="1" value="3">
                              </div>
                              
                              <div class="control-group">
                                  <label>颜色: 
                                      <select id="colorSelection">
                                          <option value="accent">主色到强调色</option>
                                          <option value="warning">主色到警告色</option>
                                          <option value="success">主色到成功色</option>
                                      </select>
                                  </label>
                              </div>
                          </div>
                          
                          <button class="play-btn" data-target="color">
                              <i class="fas fa-play"></i> 播放动画
                          </button>
                          
                          <div class="code-example">
                              <span class="comment">/* 颜色变化动画 */</span><br>
                              <span class="keyword">@keyframes</span> <span class="value">colorChange</span> {<br>
                              &nbsp;&nbsp;<span class="value">0%</span> { <span class="keyword">background-color</span>: <span class="value">var(--primary)</span>; }<br>
                              &nbsp;&nbsp;<span class="value">50%</span> { <span class="keyword">background-color</span>: <span class="value">var(--accent)</span>; }<br>
                              &nbsp;&nbsp;<span class="value">100%</span> { <span class="keyword">background-color</span>: <span class="value">var(--primary)</span>; }<br>
                              }<br><br>
                              
                              <span class="comment">/* 应用动画 */</span><br>
                              <span class="value">#colorElement</span> {<br>
                              &nbsp;&nbsp;<span class="keyword">animation</span>: <span class="value">colorChange</span> <span id="colorDurationCode">3</span>s <span class="value">ease-in-out</span> <span class="value">infinite</span>;<br>
                              }
                          </div>
                      </div>
                  </div>
              </div>
      
              <footer>
                  <p>CSS动画可视化演示 &copy; 2023</p>
                  <p>通过交互方式学习和实验CSS动画效果</p>
              </footer>
          </div>
      
          <script>
              document.addEventListener('DOMContentLoaded', function() {
                  // 动画控制按钮
                  const animControlBtns = document.querySelectorAll('.anim-control-btn');
                  const animationCards = document.querySelectorAll('.animation-card');
                  
                  // 设置动画控制按钮事件
                  animControlBtns.forEach(btn => {
                      btn.addEventListener('click', function() {
                          const animationType = this.getAttribute('data-animation');
                          
                          // 更新按钮状态
                          animControlBtns.forEach(b => b.classList.remove('active'));
                          this.classList.add('active');
                          
                          // 显示/隐藏相应的动画卡片
                          animationCards.forEach(card => {
                              if (animationType === 'all') {
                                  card.style.display = 'block';
                              } else {
                                  if (card.getAttribute('data-animation') === animationType) {
                                      card.style.display = 'block';
                                  } else {
                                      card.style.display = 'none';
                                  }
                              }
                          });
                      });
                  });
                  
                  // 淡入淡出动画控制
                  const fadeElement = document.getElementById('fadeElement');
                  const fadeDuration = document.getElementById('fadeDuration');
                  const fadeDurationValue = document.getElementById('fadeDurationValue');
                  const fadeDurationCode = document.getElementById('fadeDurationCode');
                  const fadeDelay = document.getElementById('fadeDelay');
                  const fadeDelayValue = document.getElementById('fadeDelayValue');
                  const fadeDelayCode = document.getElementById('fadeDelayCode');
                  const fadePlayBtn = document.querySelector('[data-target="fade"]');
                  
                  fadeDuration.addEventListener('input', updateFadeAnimation);
                  fadeDelay.addEventListener('input', updateFadeAnimation);
                  
                  function updateFadeAnimation() {
                      fadeDurationValue.textContent = fadeDuration.value;
                      fadeDelayValue.textContent = fadeDelay.value;
                      fadeDurationCode.textContent = fadeDuration.value;
                      fadeDelayCode.textContent = fadeDelay.value;
                  }
                  
                  fadePlayBtn.addEventListener('click', function() {
                      fadeElement.style.animation = `fade ${fadeDuration.value}s ease-in-out ${fadeDelay.value}s infinite`;
                      
                      // 重置动画
                      fadeElement.style.animation = 'none';
                      void fadeElement.offsetWidth; // 触发重绘
                      fadeElement.style.animation = `fade ${fadeDuration.value}s ease-in-out ${fadeDelay.value}s infinite`;
                  });
                  
                  // 滑动动画控制
                  const slideElement = document.getElementById('slideElement');
                  const slideDuration = document.getElementById('slideDuration');
                  const slideDurationValue = document.getElementById('slideDurationValue');
                  const slideDurationCode = document.getElementById('slideDurationCode');
                  const slideDirection = document.getElementById('slideDirection');
                  const slidePlayBtn = document.querySelector('[data-target="slide"]');
                  
                  slideDuration.addEventListener('input', updateSlideAnimation);
                  slideDirection.addEventListener('change', updateSlideAnimation);
                  
                  function updateSlideAnimation() {
                      slideDurationValue.textContent = slideDuration.value;
                      slideDurationCode.textContent = slideDuration.value;
                  }
                  
                  slidePlayBtn.addEventListener('click', function() {
                      const direction = slideDirection.value;
                      slideElement.style.animation = `slide${direction} ${slideDuration.value}s ease-out`;
                      
                      // 重置动画
                      slideElement.style.animation = 'none';
                      void slideElement.offsetWidth;
                      slideElement.style.animation = `slide${direction} ${slideDuration.value}s ease-out`;
                  });
                  
                  // 缩放动画控制
                  const scaleElement = document.getElementById('scaleElement');
                  const scaleDuration = document.getElementById('scaleDuration');
                  const scaleDurationValue = document.getElementById('scaleDurationValue');
                  const scaleDurationCode = document.getElementById('scaleDurationCode');
                  const scaleAmount = document.getElementById('scaleAmount');
                  const scaleAmountValue = document.getElementById('scaleAmountValue');
                  const scaleAmountCode = document.getElementById('scaleAmountCode');
                  const scalePlayBtn = document.querySelector('[data-target="scale"]');
                  
                  scaleDuration.addEventListener('input', updateScaleAnimation);
                  scaleAmount.addEventListener('input', updateScaleAnimation);
                  
                  function updateScaleAnimation() {
                      scaleDurationValue.textContent = scaleDuration.value;
                      scaleAmountValue.textContent = scaleAmount.value;
                      scaleDurationCode.textContent = scaleDuration.value;
                      scaleAmountCode.textContent = scaleAmount.value;
                  }
                  
                  scalePlayBtn.addEventListener('click', function() {
                      scaleElement.style.animation = `scale ${scaleDuration.value}s ease-out`;
                      
                      // 重置动画
                      scaleElement.style.animation = 'none';
                      void scaleElement.offsetWidth;
                      scaleElement.style.animation = `scale ${scaleDuration.value}s ease-out`;
                  });
                  
                  // 旋转动画控制
                  const rotateElement = document.getElementById('rotateElement');
                  const rotateDuration = document.getElementById('rotateDuration');
                  const rotateDurationValue = document.getElementById('rotateDurationValue');
                  const rotateDurationCode = document.getElementById('rotateDurationCode');
                  const rotateAmount = document.getElementById('rotateAmount');
                  const rotateAmountCode = document.getElementById('rotateAmountCode');
                  const rotatePlayBtn = document.querySelector('[data-target="rotate"]');
                  
                  rotateDuration.addEventListener('input', updateRotateAnimation);
                  rotateAmount.addEventListener('change', updateRotateAnimation);
                  
                  function updateRotateAnimation() {
                      rotateDurationValue.textContent = rotateDuration.value;
                      rotateDurationCode.textContent = rotateDuration.value;
                      rotateAmountCode.textContent = rotateAmount.value;
                  }
                  
                  rotatePlayBtn.addEventListener('click', function() {
                      rotateElement.style.animation = `rotate ${rotateDuration.value}s linear infinite`;
                      
                      // 重置动画
                      rotateElement.style.animation = 'none';
                      void rotateElement.offsetWidth;
                      rotateElement.style.animation = `rotate ${rotateDuration.value}s linear infinite`;
                  });
                  
                  // 弹跳动画控制
                  const bounceElement = document.getElementById('bounceElement');
                  const bounceDuration = document.getElementById('bounceDuration');
                  const bounceDurationValue = document.getElementById('bounceDurationValue');
                  const bounceDurationCode = document.getElementById('bounceDurationCode');
                  const bounceHeight = document.getElementById('bounceHeight');
                  const bounceHeightValue = document.getElementById('bounceHeightValue');
                  const bounceHeightCode = document.getElementById('bounceHeightCode');
                  const bouncePlayBtn = document.querySelector('[data-target="bounce"]');
                  
                  bounceDuration.addEventListener('input', updateBounceAnimation);
                  bounceHeight.addEventListener('input', updateBounceAnimation);
                  
                  function updateBounceAnimation() {
                      bounceDurationValue.textContent = bounceDuration.value;
                      bounceHeightValue.textContent = bounceHeight.value;
                      bounceDurationCode.textContent = bounceDuration.value;
                      bounceHeightCode.textContent = bounceHeight.value;
                  }
                  
                  bouncePlayBtn.addEventListener('click', function() {
                      // 动态创建弹跳动画
                      const bounceKeyframes = `
                      @keyframes bounce {
                          0%, 100% { transform: translateY(0); }
                          50% { transform: translateY(-${bounceHeight.value}px); }
                      }`;
                      
                      // 移除旧的样式
                      const oldStyle = document.getElementById('bounceAnimationStyle');
                      if (oldStyle) oldStyle.remove();
                      
                      // 添加新的样式
                      const style = document.createElement('style');
                      style.id = 'bounceAnimationStyle';
                      style.textContent = bounceKeyframes;
                      document.head.appendChild(style);
                      
                      bounceElement.style.animation = `bounce ${bounceDuration.value}s ease-in-out infinite`;
                      
                      // 重置动画
                      bounceElement.style.animation = 'none';
                      void bounceElement.offsetWidth;
                      bounceElement.style.animation = `bounce ${bounceDuration.value}s ease-in-out infinite`;
                  });
                  
                  // 颜色变化动画控制
                  const colorElement = document.getElementById('colorElement');
                  const colorDuration = document.getElementById('colorDuration');
                  const colorDurationValue = document.getElementById('colorDurationValue');
                  const colorDurationCode = document.getElementById('colorDurationCode');
                  const colorSelection = document.getElementById('colorSelection');
                  const colorPlayBtn = document.querySelector('[data-target="color"]');
                  
                  colorDuration.addEventListener('input', updateColorAnimation);
                  colorSelection.addEventListener('change', updateColorAnimation);
                  
                  function updateColorAnimation() {
                      colorDurationValue.textContent = colorDuration.value;
                      colorDurationCode.textContent = colorDuration.value;
                  }
                  
                  colorPlayBtn.addEventListener('click', function() {
                      const colorType = colorSelection.value;
                      let midColor = 'var(--accent)';
                      
                      if (colorType === 'warning') midColor = 'var(--warning)';
                      if (colorType === 'success') midColor = 'var(--success)';
                      
                      // 动态创建颜色变化动画
                      const colorKeyframes = `
                      @keyframes colorChange {
                          0% { background-color: var(--primary); }
                          50% { background-color: ${midColor}; }
                          100% { background-color: var(--primary); }
                      }`;
                      
                      // 移除旧的样式
                      const oldStyle = document.getElementById('colorAnimationStyle');
                      if (oldStyle) oldStyle.remove();
                      
                      // 添加新的样式
                      const style = document.createElement('style');
                      style.id = 'colorAnimationStyle';
                      style.textContent = colorKeyframes;
                      document.head.appendChild(style);
                      
                      colorElement.style.animation = `colorChange ${colorDuration.value}s ease-in-out infinite`;
                      
                      // 重置动画
                      colorElement.style.animation = 'none';
                      void colorElement.offsetWidth;
                      colorElement.style.animation = `colorChange ${colorDuration.value}s ease-in-out infinite`;
                  });
                  
                  // 初始化所有显示值
                  updateFadeAnimation();
                  updateSlideAnimation();
                  updateScaleAnimation();
                  updateRotateAnimation();
                  updateBounceAnimation();
                  updateColorAnimation();
              });
          </script>
      </body>
      </html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星空下的DeppBing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值