核心概念
CSS 动画和过渡都是实现元素状态变化的技术,但它们在实现方式、控制精度和应用场景上有显著区别。
Transition(过渡)
过渡是元素从一个状态到另一个状态的平滑变化过程,需要触发条件。
.element {
background: blue;
transition: background 0.3s ease;
}
.element:hover {
background: red;
}
Animation(动画)
动画是预定义的关键帧序列,可以自动播放,无需触发条件。
@keyframes slide {
0% { transform: translateX(0); }
50% { transform: translateX(100px); }
100% { transform: translateX(0); }
}
.element {
animation: slide 2s ease-in-out infinite;
}
主要区别
特性 | Transition | Animation |
---|
触发方式 | 需要状态变化触发 | 可以自动播放 |
控制精度 | 只能定义开始和结束状态 | 可以定义多个关键帧 |
循环播放 | 不支持 | 支持无限循环 |
应用场景 | 交互反馈、悬停效果 | 复杂动画、装饰效果 |
性能 | 相对较好 | 可能影响性能 |
示例说明
<!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>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background: #f5f5f5;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.demo-section {
background: white;
margin: 20px 0;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.demo-section h2 {
color: #333;
margin-bottom: 20px;
border-bottom: 2px solid #007bff;
padding-bottom: 10px;
}
.demo-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin: 20px 0;
}
.demo-item {
text-align: center;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
background: #f8f9fa;
}
.demo-item h3 {
margin-bottom: 15px;
color: #495057;
}
.transition-demo {
width: 100px;
height: 100px;
background: #007bff;
margin: 0 auto;
border-radius: 8px;
transition: all 0.3s ease;
cursor: pointer;
}
.transition-demo:hover {
background: #dc3545;
transform: scale(1.1) rotate(5deg);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
.animation-demo {
width: 80px;
height: 80px;
background: #28a745;
margin: 0 auto;
border-radius: 50%;
animation: bounce 2s ease-in-out infinite;
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-20px); }
}
.keyframe-demo {
width: 60px;
height: 60px;
background: #ffc107;
margin: 0 auto;
border-radius: 4px;
animation: slide 3s ease-in-out infinite;
}
@keyframes slide {
0% { transform: translateX(-50px); opacity: 0; }
25% { transform: translateX(0); opacity: 1; }
75% { transform: translateX(0); opacity: 1; }
100% { transform: translateX(50px); opacity: 0; }
}
.transform-3d {
perspective: 1000px;
width: 200px;
height: 200px;
margin: 0 auto;
}
.cube {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
animation: rotate3d 8s linear infinite;
}
.cube-face {
position: absolute;
width: 100px;
height: 100px;
background: rgba(0, 123, 255, 0.8);
border: 2px solid #fff;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
}
.front { transform: translateZ(50px); }
.back { transform: translateZ(-50px) rotateY(180deg); }
.right { transform: rotateY(90deg) translateZ(50px); }
.left { transform: rotateY(-90deg) translateZ(50px); }
.top { transform: rotateX(90deg) translateZ(50px); }
.bottom { transform: rotateX(-90deg) translateZ(50px); }
@keyframes rotate3d {
0% { transform: rotateX(0) rotateY(0); }
100% { transform: rotateX(360deg) rotateY(360deg); }
}
.performance-demo {
display: flex;
gap: 20px;
margin: 20px 0;
}
.optimized, .unoptimized {
width: 100px;
height: 100px;
background: #6f42c1;
border-radius: 8px;
cursor: pointer;
}
.optimized {
will-change: transform;
transition: transform 0.3s ease;
}
.optimized:hover {
transform: translateX(20px);
}
.unoptimized {
transition: left 0.3s ease;
position: relative;
left: 0;
}
.unoptimized:hover {
left: 20px;
}
.control-demo {
text-align: center;
margin: 20px 0;
}
.controlled-animation {
width: 100px;
height: 100px;
background: #fd7e14;
margin: 20px auto;
border-radius: 8px;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.2); opacity: 0.7; }
}
.control-buttons {
margin: 15px 0;
}
.control-btn {
padding: 8px 16px;
margin: 0 5px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background 0.3s;
}
.control-btn:hover {
background: #0056b3;
}
.control-btn:disabled {
background: #6c757d;
cursor: not-allowed;
}
.code-example {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
padding: 15px;
margin: 15px 0;
font-family: 'Courier New', monospace;
font-size: 14px;
overflow-x: auto;
}
.comparison-table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
.comparison-table th,
.comparison-table td {
border: 1px solid #dee2e6;
padding: 10px;
text-align: left;
}
.comparison-table th {
background: #f8f9fa;
font-weight: bold;
}
.pros {
color: #28a745;
}
.cons {
color: #dc3545;
}
</style>
</head>
<body>
<div class="container">
<h1>CSS 动画与过渡演示</h1>
<section class="demo-section">
<h2>过渡 vs 动画对比</h2>
<div class="demo-grid">
<div class="demo-item">
<h3>Transition(过渡)</h3>
<div class="transition-demo"></div>
<p>悬停触发,平滑状态变化</p>
</div>
<div class="demo-item">
<h3>Animation(动画)</h3>
<div class="animation-demo"></div>
<p>自动播放,关键帧控制</p>
</div>
</div>
<table class="comparison-table">
<thead>
<tr>
<th>特性</th>
<th>Transition</th>
<th>Animation</th>
</tr>
</thead>
<tbody>
<tr>
<td>触发方式</td>
<td class="pros">需要状态变化触发</td>
<td class="pros">可以自动播放</td>
</tr>
<tr>
<td>控制精度</td>
<td class="cons">只能定义开始和结束状态</td>
<td class="pros">可以定义多个关键帧</td>
</tr>
<tr>
<td>循环播放</td>
<td class="cons">不支持</td>
<td class="pros">支持无限循环</td>
</tr>
<tr>
<td>应用场景</td>
<td>交互反馈、悬停效果</td>
<td>复杂动画、装饰效果</td>
</tr>
</tbody>
</table>
</section>
<section class="demo-section">
<h2>关键帧动画演示</h2>
<div class="demo-item">
<h3>滑动动画</h3>
<div class="keyframe-demo"></div>
<p>使用 @keyframes 定义复杂动画序列</p>
</div>
<div class="code-example">
@keyframes slide {
0% { transform: translateX(-50px); opacity: 0; }
25% { transform: translateX(0); opacity: 1; }
75% { transform: translateX(0); opacity: 1; }
100% { transform: translateX(50px); opacity: 0; }
}
.keyframe-demo {
animation: slide 3s ease-in-out infinite;
}
</div>
</section>
<section class="demo-section">
<h2>3D 变换演示</h2>
<div class="transform-3d">
<div class="cube">
<div class="cube-face front">前</div>
<div class="cube-face back">后</div>
<div class="cube-face right">右</div>
<div class="cube-face left">左</div>
<div class="cube-face top">上</div>
<div class="cube-face bottom">下</div>
</div>
</div>
<div class="code-example">
.transform-3d {
perspective: 1000px;
}
.cube {
transform-style: preserve-3d;
animation: rotate3d 8s linear infinite;
}
@keyframes rotate3d {
0% { transform: rotateX(0) rotateY(0); }
100% { transform: rotateX(360deg) rotateY(360deg); }
}
</div>
</section>
<section class="demo-section">
<h2>性能优化演示</h2>
<div class="performance-demo">
<div>
<h3>优化版本(推荐)</h3>
<div class="optimized"></div>
<p>使用 transform,GPU 加速</p>
</div>
<div>
<h3>未优化版本</h3>
<div class="unoptimized"></div>
<p>使用 left 属性,触发重排</p>
</div>
</div>
<div class="code-example">
/* 优化版本 */
.optimized {
will-change: transform;
transition: transform 0.3s ease;
}
.optimized:hover {
transform: translateX(20px);
}
/* 未优化版本 */
.unoptimized {
transition: left 0.3s ease;
position: relative;
left: 0;
}
.unoptimized:hover {
left: 20px;
}
</div>
</section>
<section class="demo-section">
<h2>动画控制演示</h2>
<div class="control-demo">
<div class="controlled-animation"></div>
<div class="control-buttons">
<button class="control-btn" onclick="playAnimation()">播放</button>
<button class="control-btn" onclick="pauseAnimation()">暂停</button>
<button class="control-btn" onclick="resetAnimation()">重置</button>
</div>
</div>
<div class="code-example">
// JavaScript 控制动画
function playAnimation() {
element.style.animationPlayState = 'running';
}
function pauseAnimation() {
element.style.animationPlayState = 'paused';
}
// 监听动画事件
element.addEventListener('animationend', () => {
console.log('动画结束');
});
</div>
</section>
<section class="demo-section">
<h2>动画最佳实践</h2>
<ul>
<li><strong>使用 GPU 加速属性</strong>:transform、opacity、filter</li>
<li><strong>避免触发重排</strong>:width、height、margin、padding</li>
<li><strong>合理使用 will-change</strong>:提前告知浏览器优化</li>
<li><strong>控制动画时长</strong>:保持 60fps 的流畅度</li>
<li><strong>考虑用户体验</strong>:提供暂停和减少动画的选项</li>
</ul>
</section>
</div>
<script>
const controlledAnimation = document.querySelector('.controlled-animation');
function playAnimation() {
controlledAnimation.style.animationPlayState = 'running';
}
function pauseAnimation() {
controlledAnimation.style.animationPlayState = 'paused';
}
function resetAnimation() {
controlledAnimation.style.animation = 'none';
setTimeout(() => {
controlledAnimation.style.animation = 'pulse 2s ease-in-out infinite';
}, 10);
}
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
console.log('动画性能:', entry.duration, 'ms');
});
});
try {
observer.observe({ entryTypes: ['animation'] });
} catch (e) {
console.log('性能监控不可用');
}
}
document.addEventListener('DOMContentLoaded', function() {
const demoItems = document.querySelectorAll('.demo-item');
demoItems.forEach(item => {
item.addEventListener('mouseenter', function() {
this.style.transform = 'translateY(-5px)';
this.style.transition = 'transform 0.3s ease';
});
item.addEventListener('mouseleave', function() {
this.style.transform = 'translateY(0)';
});
});
});
</script>
</body>
</html>
面试官视角
该题考察对 CSS 动画技术的理解,体现了候选人对用户体验和交互设计的重视。
延伸阅读