跳到主要内容

动画与过渡

CSS 动画和过渡有什么区别?

答案

核心概念

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;
}

主要区别

特性TransitionAnimation
触发方式需要状态变化触发可以自动播放
控制精度只能定义开始和结束状态可以定义多个关键帧
循环播放不支持支持无限循环
应用场景交互反馈、悬停效果复杂动画、装饰效果
性能相对较好可能影响性能

示例说明

<!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; }
        }
        
        /* 3D 变换演示 */
        .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>
        
        <!-- 过渡 vs 动画对比 -->
        <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>
        
        <!-- 3D 变换演示 -->
        <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>

Open browser consoleTests

面试官视角

该题考察对 CSS 动画技术的理解,体现了候选人对用户体验和交互设计的重视。

延伸阅读

如何实现 CSS 动画的性能优化?

答案

核心概念

CSS 动画性能优化是前端开发中的重要话题,主要涉及 GPU 加速、属性选择和动画策略。

优化策略

  1. 使用 GPU 加速属性

    /* 推荐:使用 transform 和 opacity */
    .optimized {
    transform: translateX(100px);
    opacity: 0.5;
    }

    /* 避免:使用会触发重排的属性 */
    .unoptimized {
    left: 100px;
    width: 200px;
    }
  2. 避免触发重排的属性

    • 避免使用:widthheightmarginpaddingborder
    • 推荐使用:transformopacityfilter
  3. 使用 will-change 属性

    .element {
    will-change: transform;
    transform: translateZ(0);
    }
  4. 合理使用动画时长

    /* 推荐:60fps 的动画时长 */
    .smooth {
    transition: transform 0.16s ease-out;
    }

性能监控

// 检测动画性能
const element = document.querySelector('.animated')
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
console.log('动画性能:', entry.duration)
})
})
observer.observe({ entryTypes: ['animation'] })

最佳实践

  1. 分层动画

    /* 将动画分层,避免相互影响 */
    .layer-1 { transform: translateZ(0); }
    .layer-2 { transform: translateZ(1px); }
    .layer-3 { transform: translateZ(2px); }
  2. 使用 requestAnimationFrame

    function animate () {
    // 动画逻辑
    requestAnimationFrame(animate)
    }
    requestAnimationFrame(animate)
  3. 避免动画冲突

    /* 使用 transform3d 强制 GPU 加速 */
    .gpu-accelerated {
    transform: translate3d(0, 0, 0);
    }

面试官视角

该题考察对前端性能优化的理解,体现了候选人对用户体验和性能优化的重视。

延伸阅读

如何实现 CSS 关键帧动画?

答案

核心概念

CSS 关键帧动画通过 @keyframes 规则定义动画序列,可以精确控制动画的每个阶段。

基本语法

@keyframes animationName {
0% { /* 初始状态 */ }
50% { /* 中间状态 */ }
100% { /* 结束状态 */ }
}

.element {
animation: animationName 2s ease-in-out infinite;
}

关键帧定义方式

  1. 百分比定义

    @keyframes slide {
    0% { transform: translateX(0); }
    25% { transform: translateX(100px); }
    75% { transform: translateX(-50px); }
    100% { transform: translateX(0); }
    }
  2. 关键字定义

    @keyframes fade {
    from { opacity: 0; }
    to { opacity: 1; }
    }

动画属性详解

.element {
animation-name: slide; /* 动画名称 */
animation-duration: 2s; /* 动画时长 */
animation-timing-function: ease; /* 缓动函数 */
animation-delay: 0s; /* 延迟时间 */
animation-iteration-count: 1; /* 重复次数 */
animation-direction: normal; /* 播放方向 */
animation-fill-mode: none; /* 填充模式 */
animation-play-state: running; /* 播放状态 */
}

常用动画效果

  1. 淡入淡出

    @keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
    }
  2. 滑动效果

    @keyframes slideIn {
    from { transform: translateX(-100%); }
    to { transform: translateX(0); }
    }
  3. 缩放效果

    @keyframes scale {
    0% { transform: scale(1); }
    50% { transform: scale(1.2); }
    100% { transform: scale(1); }
    }

动画控制

// 暂停动画
element.style.animationPlayState = 'paused'

// 恢复动画
element.style.animationPlayState = 'running'

// 监听动画事件
element.addEventListener('animationend', () => {
console.log('动画结束')
})

面试官视角

该题考察对 CSS 动画技术的深入理解,体现了候选人对交互设计和用户体验的重视。

延伸阅读

如何实现 CSS 3D 变换?

答案

核心概念

CSS 3D 变换允许在三维空间中操作元素,创建更丰富的视觉效果和交互体验。

3D 变换属性

  1. 透视(Perspective)

    .container {
    perspective: 1000px;
    }
  2. 3D 变换函数

    .element {
    transform: rotateX(45deg); /* X轴旋转 */
    transform: rotateY(45deg); /* Y轴旋转 */
    transform: rotateZ(45deg); /* Z轴旋转 */
    transform: translateZ(100px); /* Z轴平移 */
    transform: scaleZ(2); /* Z轴缩放 */
    }
  3. 变换样式

    .element {
    transform-style: preserve-3d; /* 保持3D效果 */
    backface-visibility: hidden; /* 隐藏背面 */
    }

常见 3D 效果

  1. 卡片翻转

    .card {
    perspective: 1000px;
    }

    .card-inner {
    transition: transform 0.6s;
    transform-style: preserve-3d;
    }

    .card:hover .card-inner {
    transform: rotateY(180deg);
    }
  2. 立方体

    .cube {
    transform-style: preserve-3d;
    animation: rotate 10s infinite linear;
    }

    @keyframes rotate {
    from { transform: rotateX(0) rotateY(0); }
    to { transform: rotateX(360deg) rotateY(360deg); }
    }
  3. 视差效果

    .parallax {
    transform: translateZ(-100px) scale(2);
    }

性能优化

  1. 使用 transform3d

    .optimized {
    transform: translate3d(0, 0, 0);
    }
  2. 合理设置透视

    .container {
    perspective: 1000px;
    perspective-origin: center;
    }

面试官视角

该题考察对高级 CSS 技术的理解,体现了候选人对现代前端技术的掌握程度。

延伸阅读

55%