
《中级前端进阶方向 第一步)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 捕获》
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):
-
animationstart、animationiteration、animationend(可监听 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)。
-
推荐只动画化
transform与opacity—— 它们通常只会触发合成层(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-delay和nth-child()或 CSS 变量计算:
.item { animation: pop .4s cubic-bezier(...) both; animation-delay: calc(var(--i) * 80ms); }
-
序列化(chain):监听
animationend或使用 WAAPI 的finishedpromise,开始下一个动画。 -
sprite 帧动画:用
steps()+ background-position 逐帧显示。 -
3D flip:使用
transform-style: preserve-3d、backface-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 可视化源代码
演示页面包含以下核心功能:
-
六种动画类型:
-
淡入淡出 (Fade)
-
滑动 (Slide)
-
缩放 (Scale)
-
旋转 (Rotate)
-
弹跳 (Bounce)
-
颜色变化 (Color Change)
-
-
交互式控制:
-
每个动画都有可调整的参数(持续时间、延迟、方向等)
-
实时更新动画效果和代码示例
-
播放按钮可以重新触发动画
-
-
分类筛选:
-
可以通过顶部按钮筛选显示特定类型的动画
-
-
实时代码生成:
-
每个动画都显示对应的CSS代码
-
代码会根据参数调整实时更新
-
-
响应式设计:
-
页面适配各种屏幕尺寸
<!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> <span class="value">0%</span> { <span class="keyword">opacity</span>: <span class="value">0</span>; }<br> <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> <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> <span class="value">0%</span> { <span class="keyword">transform</span>: <span class="value">translateX(-100px)</span>; }<br> <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> <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> <span class="value">0%</span> { <span class="keyword">transform</span>: <span class="value">scale(<span id="scaleAmountCode">0.5</span>)</span>; }<br> <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> <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> <span class="value">0%</span> { <span class="keyword">transform</span>: <span class="value">rotate(0deg)</span>; }<br> <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> <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> <span class="value">0%, 100%</span> { <span class="keyword">transform</span>: <span class="value">translateY(0)</span>; }<br> <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> <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> <span class="value">0%</span> { <span class="keyword">background-color</span>: <span class="value">var(--primary)</span>; }<br> <span class="value">50%</span> { <span class="keyword">background-color</span>: <span class="value">var(--accent)</span>; }<br> <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> <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动画可视化演示 © 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>
-
1205

被折叠的 条评论
为什么被折叠?



