Impress.js 3D立方体旋转个人年终总结设计与实现

摘要

本文将详细介绍如何使用Impress.js创建一个具有艺术性的3D立方体旋转个人年终总结展示。通过结合CSS3 3D变换、动画效果、图表可视化等现代Web技术,设计一个既实用又具有视觉冲击力的交互式演示文稿。本文包含完整代码实现、设计理念、技术分析和教学指导,帮助读者掌握创建高质量3D演示文稿的技能。

1. 项目概述与设计理念

1.1 项目目标

本项目旨在创建一个具有以下特点的年终总结演示:

  1. 3D立方体导航:通过旋转立方体在六个不同主题之间切换

  2. 艺术性设计:精心设计的字体、颜色、动画和视觉效果

  3. 数据可视化:整合图表和进度指示器展示年度数据

  4. 响应式布局:适配不同设备和屏幕尺寸

  5. 交互体验:键盘、鼠标、触摸等多种交互方式

  6. 离线运行:所有资源本地化,无需外部依赖

1.2 设计理念

1.3 技术架构

2. 完整代码实现

2.1 HTML结构

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2024年度总结 - 3D立方体展示</title>
    
    <!-- 内联Impress.js简化版 -->
    <script>
        (function(document, window) {
            'use strict';
            
            // 完整的Impress.js实现
            window.impress = function(rootId) {
                rootId = rootId || 'impress';
                var root = document.getElementById(rootId);
                var steps = root.querySelectorAll('.step');
                var currentStep = 0;
                var canvas = document.createElement('div');
                var isInitialized = false;
                
                canvas.className = 'impress-canvas';
                canvas.style.cssText = 
                    'position:absolute;top:0;left:0;width:100%;height:100%;' +
                    'transform-style:preserve-3d;transition:transform 1s ease-in-out;';
                
                root.appendChild(canvas);
                
                // 初始化步骤位置
                function initSteps() {
                    steps.forEach(function(step, i) {
                        var x = step.getAttribute('data-x') || 0;
                        var y = step.getAttribute('data-y') || 0;
                        var z = step.getAttribute('data-z') || 0;
                        var rotateX = step.getAttribute('data-rotate-x') || 0;
                        var rotateY = step.getAttribute('data-rotate-y') || 0;
                        var rotateZ = step.getAttribute('data-rotate-z') || 0;
                        var scale = step.getAttribute('data-scale') || 1;
                        
                        step.style.cssText = 
                            'position:absolute;transform-style:preserve-3d;' +
                            'transform:translate3d(' + x + 'px,' + y + 'px,' + z + 'px) ' +
                            'rotateX(' + rotateX + 'deg) rotateY(' + rotateY + 'deg) rotateZ(' + rotateZ + 'deg) ' +
                            'scale(' + scale + ');';
                    });
                }
                
                // 跳转到指定步骤
                function goto(index, duration) {
                    if (index < 0 || index >= steps.length) return;
                    
                    var step = steps[index];
                    var target = {
                        x: -(step.getAttribute('data-x') || 0),
                        y: -(step.getAttribute('data-y') || 0),
                        z: -(step.getAttribute('data-z') || 0),
                        rotateX: -(step.getAttribute('data-rotate-x') || 0),
                        rotateY: -(step.getAttribute('data-rotate-y') || 0),
                        rotateZ: -(step.getAttribute('data-rotate-z') || 0),
                        scale: 1 / (step.getAttribute('data-scale') || 1)
                    };
                    
                    var transform = 
                        'translate3d(' + target.x + 'px,' + target.y + 'px,' + target.z + 'px) ' +
                        'rotateX(' + target.rotateX + 'deg) rotateY(' + target.rotateY + 'deg) rotateZ(' + target.rotateZ + 'deg) ' +
                        'scale(' + target.scale + ')';
                    
                    canvas.style.transition = 'transform ' + (duration || 1000) + 'ms cubic-bezier(0.175, 0.885, 0.32, 1.275)';
                    canvas.style.transform = transform;
                    
                    // 触发事件
                    if (steps[currentStep]) {
                        steps[currentStep].classList.remove('active');
                        var leaveEvent = new CustomEvent('impress:stepleave');
                        steps[currentStep].dispatchEvent(leaveEvent);
                    }
                    
                    step.classList.add('active');
                    currentStep = index;
                    var enterEvent = new CustomEvent('impress:stepenter');
                    step.dispatchEvent(enterEvent);
                }
                
                var api = {
                    init: function() {
                        if (isInitialized) return this;
                        isInitialized = true;
                        
                        initSteps();
                        
                        var hash = window.location.hash.replace(/^#\/?/, '');
                        var initialStep = document.getElementById(hash) || steps[0];
                        var initialIndex = Array.from(steps).indexOf(initialStep);
                        goto(initialIndex, 0);
                        
                        // 键盘控制
                        document.addEventListener('keydown', function(e) {
                            if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
                            
                            switch(e.key) {
                                case 'ArrowLeft':
                                case 'a':
                                case 'A':
                                    e.preventDefault();
                                    api.prev();
                                    break;
                                case 'ArrowRight':
                                case 'd':
                                case 'D':
                                    e.preventDefault();
                                    api.next();
                                    break;
                                case ' ':
                                case 'Enter':
                                    e.preventDefault();
                                    api.next();
                                    break;
                                case 'Escape':
                                    e.preventDefault();
                                    goto(0);
                                    break;
                            }
                        });
                        
                        // 窗口调整
                        window.addEventListener('resize', function() {
                            var scale = Math.min(
                                window.innerWidth / root.offsetWidth,
                                window.innerHeight / root.offsetHeight
                            );
                            root.style.transform = 'scale(' + scale + ')';
                        });
                        
                        var initEvent = new CustomEvent('impress:init');
                        root.dispatchEvent(initEvent);
                        
                        return this;
                    },
                    
                    goto: function(step, duration) {
                        if (typeof step === 'string') {
                            step = document.getElementById(step);
                        }
                        var index = Array.from(steps).indexOf(step);
                        if (index >= 0) {
                            goto(index, duration);
                        }
                        return this;
                    },
                    
                    next: function() {
                        goto(currentStep + 1);
                        return this;
                    },
                    
                    prev: function() {
                        goto(currentStep - 1);
                        return this;
                    },
                    
                    getCurrentStep: function() {
                        return steps[currentStep];
                    }
                };
                
                return api;
            };
        })(document, window);
    </script>
    
    <style>
        /* 2.2 CSS样式将在下一节展示 */
    </style>
</head>
<body>
    <!-- 加载屏幕 -->
    <div class="loading-screen" id="loadingScreen">
        <div class="loading-content">
            <div class="loading-spinner">
                <div class="cube">
                    <div class="face front"></div>
                    <div class="face back"></div>
                    <div class="face right"></div>
                    <div class="face left"></div>
                    <div class="face top"></div>
                    <div class="face bottom"></div>
                </div>
            </div>
            <div class="loading-text">
                <div class="loading-title">加载中</div>
                <div class="loading-subtitle">准备您的年度总结之旅</div>
            </div>
        </div>
    </div>
    
    <!-- 主容器 -->
    <div id="impress">
        <!-- 封面页 -->
        <div class="step" id="cover" data-x="0" data-y="0" data-z="0" data-rotate-x="0" data-rotate-y="0" data-scale="1">
            <div class="cover-container">
                <div class="cube-frame">
                    <div class="cube" id="cube-animation">
                        <div class="cube-face front">2024</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="cover-content">
                    <h1 class="cover-title">2024年度总结</h1>
                    <p class="cover-subtitle">探索、成长、突破</p>
                    <div class="cover-stats">
                        <div class="stat-item">
                            <div class="stat-number" id="daysCount">0</div>
                            <div class="stat-label">天旅程</div>
                        </div>
                        <div class="stat-item">
                            <div class="stat-number" id="projectsCount">0</div>
                            <div class="stat-label">个项目</div>
                        </div>
                        <div class="stat-item">
                            <div class="stat-number" id="growthCount">0%</div>
                            <div class="stat-label">成长</div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 工作成果页 -->
        <div class="step" id="achievements" data-x="0" data-y="0" data-z="1000" data-rotate-y="0" data-scale="1">
            <div class="content-container">
                <div class="section-header">
                    <h2 class="section-title">工作成果</h2>
                    <div class="section-subtitle">2024年主要项目与里程碑</div>
                </div>
                
                <div class="timeline">
                    <div class="timeline-item">
                        <div class="timeline-date">Q1</div>
                        <div class="timeline-content">
                            <h3>项目Alpha</h3>
                            <p>完成了系统架构重构,性能提升40%</p>
                            <div class="progress-bar">
                                <div class="progress-fill" style="width: 85%"></div>
                            </div>
                        </div>
                    </div>
                    <div class="timeline-item">
                        <div class="timeline-date">Q2</div>
                        <div class="timeline-content">
                            <h3>项目Beta</h3>
                            <p>推出新功能模块,用户增长200%</p>
                            <div class="progress-bar">
                                <div class="progress-fill" style="width: 92%"></div>
                            </div>
                        </div>
                    </div>
                </div>
                
                <div class="chart-container">
                    <div class="chart-title">季度绩效对比</div>
                    <canvas id="performanceChart" width="400" height="200"></canvas>
                </div>
            </div>
        </div>

        <!-- 技能成长页 -->
        <div class="step" id="skills" data-x="1000" data-y="0" data-z="0" data-rotate-y="-90" data-scale="1">
            <div class="content-container">
                <div class="section-header">
                    <h2 class="section-title">技能成长</h2>
                    <div class="section-subtitle">2024年技能提升图谱</div>
                </div>
                
                <div class="radar-chart">
                    <canvas id="skillsChart" width="300" height="300"></canvas>
                </div>
                
                <div class="skills-grid">
                    <div class="skill-card">
                        <div class="skill-icon">🚀</div>
                        <h4>前端开发</h4>
                        <div class="skill-level">
                            <div class="level-bar">
                                <div class="level-fill" style="width: 85%"></div>
                            </div>
                            <span>85%</span>
                        </div>
                    </div>
                    <div class="skill-card">
                        <div class="skill-icon">🎨</div>
                        <h4>UI/UX设计</h4>
                        <div class="skill-level">
                            <div class="level-bar">
                                <div class="level-fill" style="width: 70%"></div>
                            </div>
                            <span>70%</span>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 学习收获页 -->
        <div class="step" id="learning" data-x="-1000" data-y="0" data-z="0" data-rotate-y="90" data-scale="1">
            <div class="content-container">
                <div class="section-header">
                    <h2 class="section-title">学习收获</h2>
                    <div class="section-subtitle">知识积累与技术探索</div>
                </div>
                
                <div class="book-shelf">
                    <div class="book" style="--color: #ff6b6b;">
                        <div class="book-spine">AI</div>
                        <div class="book-cover">机器学习</div>
                    </div>
                    <div class="book" style="--color: #4ecdc4;">
                        <div class="book-spine">Web3</div>
                        <div class="book-cover">区块链</div>
                    </div>
                </div>
                
                <div class="stats-cards">
                    <div class="stat-card">
                        <div class="stat-number">12</div>
                        <div class="stat-label">在线课程</div>
                    </div>
                    <div class="stat-card">
                        <div class="stat-number">24</div>
                        <div class="stat-label">技术文章</div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 生活平衡页 -->
        <div class="step" id="life" data-x="0" data-y="-1000" data-z="0" data-rotate-x="-90" data-scale="1">
            <div class="content-container">
                <div class="section-header">
                    <h2 class="section-title">生活平衡</h2>
                    <div class="section-subtitle">工作与生活的和谐之道</div>
                </div>
                
                <div class="balance-wheel">
                    <div class="wheel-item" style="--percent: 40;">工作</div>
                    <div class="wheel-item" style="--percent: 25;">学习</div>
                    <div class="wheel-item" style="--percent: 20;">健康</div>
                </div>
                
                <div class="habit-tracker">
                    <div class="habit">
                        <span>运动</span>
                        <div class="habit-dots">
                            <div class="dot active"></div>
                            <div class="dot active"></div>
                            <!-- 更多点 -->
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- 未来规划页 -->
        <div class="step" id="future" data-x="0" data-y="1000" data-z="0" data-rotate-x="90" data-scale="1">
            <div class="content-container">
                <div class="section-header">
                    <h2 class="section-title">未来规划</h2>
                    <div class="section-subtitle">2025年目标与展望</div>
                </div>
                
                <div class="goal-list">
                    <div class="goal-item">
                        <div class="goal-checkbox"></div>
                        <div class="goal-content">
                            <h3>技术深度</h3>
                            <p>深入研究Web3与AI结合</p>
                        </div>
                    </div>
                </div>
                
                <div class="roadmap">
                    <div class="roadmap-item">Q1 基础</div>
                    <div class="roadmap-item">Q2 实践</div>
                </div>
            </div>
        </div>
    </div>
    
    <!-- 导航控制 -->
    <div class="navigation">
        <button class="nav-btn" onclick="rotateCube('prev')">←</button>
        <button class="nav-btn" onclick="gotoStep('cover')">封面</button>
        <button class="nav-btn" onclick="gotoStep('achievements')">成果</button>
        <button class="nav-btn" onclick="gotoStep('skills')">技能</button>
        <button class="nav-btn" onclick="gotoStep('learning')">学习</button>
        <button class="nav-btn" onclick="gotoStep('life')">生活</button>
        <button class="nav-btn" onclick="gotoStep('future')">未来</button>
        <button class="nav-btn" onclick="rotateCube('next')">→</button>
    </div>
    
    <!-- 进度指示 -->
    <div class="progress-indicator">
        <div class="progress-dot active" data-step="cover"></div>
        <div class="progress-dot" data-step="achievements"></div>
        <div class="progress-dot" data-step="skills"></div>
        <div class="progress-dot" data-step="learning"></div>
        <div class="progress-dot" data-step="life"></div>
        <div class="progress-dot" data-step="future"></div>
    </div>

    <script>
        // 2.3 JavaScript逻辑将在下一节展示
    </script>
</body>
</html>

2.2 CSS样式设计

css 复制代码
/* 基础重置与变量定义 */
:root {
    /* 色彩系统 - 基于情感心理学设计 */
    --primary: #667eea;
    --primary-dark: #764ba2;
    --secondary: #4ecdc4;
    --secondary-dark: #44a08d;
    --accent: #ff6b6b;
    --accent-dark: #ee5a5a;
    --success: #51cf66;
    --warning: #ffd43b;
    --danger: #ff6b6b;
    --info: #339af0;
    
    /* 中性色 - 增强可读性 */
    --text-primary: #2d3436;
    --text-secondary: #636e72;
    --text-tertiary: #b2bec3;
    --bg-primary: #ffffff;
    --bg-secondary: #f8f9fa;
    --bg-tertiary: #e9ecef;
    --border-color: #dee2e6;
    
    /* 渐变系统 - 创造视觉深度 */
    --gradient-primary: linear-gradient(135deg, var(--primary), var(--primary-dark));
    --gradient-secondary: linear-gradient(135deg, var(--secondary), var(--secondary-dark));
    --gradient-accent: linear-gradient(135deg, var(--accent), var(--accent-dark));
    
    /* 阴影系统 - 创建3D效果 */
    --shadow-sm: 0 2px 4px rgba(0,0,0,0.1);
    --shadow-md: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -1px rgba(0,0,0,0.06);
    --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05);
    --shadow-xl: 0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04);
    
    /* 字体系统 - 增强可读性 */
    --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
    --font-serif: Georgia, Cambria, "Times New Roman", Times, serif;
    --font-mono: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
    
    /* 间距系统 - 8px基准 */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-6: 1.5rem;
    --space-8: 2rem;
    --space-12: 3rem;
    --space-16: 4rem;
    
    /* 动画曲线 - 增强交互感 */
    --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
    --ease-out: cubic-bezier(0, 0, 0.2, 1);
    --ease-in: cubic-bezier(0.4, 0, 1, 1);
    --bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

/* 基础重置 */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html {
    font-size: 16px;
    scroll-behavior: smooth;
}

body {
    font-family: var(--font-sans);
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: var(--text-primary);
    min-height: 100vh;
    overflow: hidden;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Impress.js容器 */
#impress {
    width: 100vw;
    height: 100vh;
    perspective: 2000px;
    position: relative;
    overflow: hidden;
}

.impress-canvas {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    transform-style: preserve-3d;
    transition: transform 1s var(--ease-in-out);
}

/* 步骤基础样式 */
.step {
    position: absolute;
    width: 800px;
    height: 600px;
    padding: var(--space-8);
    background: rgba(255, 255, 255, 0.95);
    backdrop-filter: blur(20px);
    border-radius: 24px;
    border: 1px solid rgba(255, 255, 255, 0.2);
    box-shadow: var(--shadow-xl), 0 8px 32px rgba(0, 0, 0, 0.1);
    transition: all 0.6s var(--ease-in-out);
    opacity: 0.7;
    transform-origin: center center;
}

.step.active {
    opacity: 1;
    transform: translateZ(50px);
    box-shadow: var(--shadow-xl), 0 20px 60px rgba(0, 0, 0, 0.2);
}

/* 加载屏幕 */
.loading-screen {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: var(--bg-primary);
    display: flex;
    justify-content: center;
    align-items: center;
    z-index: 9999;
    transition: opacity 0.8s var(--ease-in-out);
}

.loading-screen.hidden {
    opacity: 0;
    pointer-events: none;
}

.loading-content {
    text-align: center;
    animation: fadeInUp 0.8s var(--ease-out);
}

.loading-spinner {
    width: 120px;
    height: 120px;
    margin: 0 auto var(--space-8);
    perspective: 1000px;
}

.loading-spinner .cube {
    width: 100%;
    height: 100%;
    position: relative;
    transform-style: preserve-3d;
    animation: rotateCube 3s infinite linear;
}

.loading-spinner .face {
    position: absolute;
    width: 120px;
    height: 120px;
    background: linear-gradient(45deg, var(--primary), var(--secondary));
    border: 2px solid rgba(255, 255, 255, 0.1);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 2rem;
    font-weight: bold;
    color: white;
    opacity: 0.8;
}

.loading-spinner .front  { transform: translateZ(60px); }
.loading-spinner .back   { transform: translateZ(-60px) rotateY(180deg); }
.loading-spinner .right  { transform: translateX(60px) rotateY(90deg); }
.loading-spinner .left   { transform: translateX(-60px) rotateY(-90deg); }
.loading-spinner .top    { transform: translateY(-60px) rotateX(90deg); }
.loading-spinner .bottom { transform: translateY(60px) rotateX(-90deg); }

/* 封面页设计 */
.cover-container {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-8);
}

.cube-frame {
    width: 300px;
    height: 300px;
    perspective: 1200px;
    margin-bottom: var(--space-8);
}

#cube-animation {
    width: 100%;
    height: 100%;
    position: relative;
    transform-style: preserve-3d;
    animation: floatCube 6s infinite var(--ease-in-out);
}

.cube-face {
    position: absolute;
    width: 300px;
    height: 300px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 3rem;
    font-weight: 900;
    color: white;
    border-radius: 12px;
    backdrop-filter: blur(10px);
    transition: all 0.3s var(--ease-out);
}

.cube-face:hover {
    transform: translateZ(20px);
    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
}

.cube-face.front {
    background: linear-gradient(135deg, var(--primary), var(--primary-dark));
    transform: translateZ(150px);
}

.cube-face.back {
    background: linear-gradient(135deg, var(--secondary), var(--secondary-dark));
    transform: translateZ(-150px) rotateY(180deg);
}

.cube-face.right {
    background: linear-gradient(135deg, var(--accent), var(--accent-dark));
    transform: translateX(150px) rotateY(90deg);
}

.cube-face.left {
    background: linear-gradient(135deg, #51cf66, #40c057);
    transform: translateX(-150px) rotateY(-90deg);
}

.cube-face.top {
    background: linear-gradient(135deg, #ffd43b, #fab005);
    transform: translateY(-150px) rotateX(90deg);
}

.cube-face.bottom {
    background: linear-gradient(135deg, #339af0, #228be6);
    transform: translateY(150px) rotateX(-90deg);
}

.cover-content {
    text-align: center;
    animation: fadeInUp 0.8s var(--ease-out) 0.3s both;
}

.cover-title {
    font-size: 4rem;
    font-weight: 900;
    background: linear-gradient(135deg, var(--primary), var(--secondary));
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    margin-bottom: var(--space-4);
    text-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.cover-subtitle {
    font-size: 1.5rem;
    color: var(--text-secondary);
    margin-bottom: var(--space-8);
    font-weight: 300;
    letter-spacing: 2px;
}

.cover-stats {
    display: flex;
    gap: var(--space-8);
    margin-top: var(--space-8);
}

.stat-item {
    text-align: center;
    animation: slideUp 0.6s var(--ease-out) 0.5s both;
}

.stat-number {
    font-size: 3rem;
    font-weight: 900;
    background: linear-gradient(135deg, var(--accent), var(--primary));
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    margin-bottom: var(--space-2);
}

.stat-label {
    font-size: 0.9rem;
    color: var(--text-tertiary);
    text-transform: uppercase;
    letter-spacing: 1px;
}

/* 内容容器样式 */
.content-container {
    width: 100%;
    height: 100%;
    padding: var(--space-8);
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.section-header {
    text-align: center;
    margin-bottom: var(--space-6);
    animation: fadeIn 0.6s var(--ease-out);
}

.section-title {
    font-size: 2.5rem;
    font-weight: 800;
    background: linear-gradient(135deg, var(--primary), var(--secondary));
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    margin-bottom: var(--space-2);
}

.section-subtitle {
    font-size: 1.1rem;
    color: var(--text-secondary);
    font-weight: 300;
}

/* 时间线组件 */
.timeline {
    position: relative;
    padding-left: var(--space-8);
    margin: var(--space-6) 0;
}

.timeline::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 2px;
    background: linear-gradient(to bottom, var(--primary), var(--secondary));
}

.timeline-item {
    position: relative;
    margin-bottom: var(--space-6);
    padding-left: var(--space-6);
    animation: slideInRight 0.6s var(--ease-out);
}

.timeline-item::before {
    content: '';
    position: absolute;
    left: -8px;
    top: 8px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--primary);
    border: 3px solid white;
    box-shadow: 0 0 0 4px var(--primary);
}

.timeline-date {
    position: absolute;
    left: -80px;
    top: 0;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--primary);
    background: white;
    padding: var(--space-1) var(--space-3);
    border-radius: 20px;
    border: 2px solid var(--primary);
}

.timeline-content h3 {
    font-size: 1.2rem;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: var(--space-2);
}

.timeline-content p {
    color: var(--text-secondary);
    margin-bottom: var(--space-3);
}

/* 进度条 */
.progress-bar {
    width: 100%;
    height: 8px;
    background: var(--bg-tertiary);
    border-radius: 4px;
    overflow: hidden;
    margin-top: var(--space-3);
}

.progress-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--primary), var(--secondary));
    border-radius: 4px;
    transition: width 1s var(--ease-in-out);
    position: relative;
    overflow: hidden;
}

.progress-fill::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
    animation: shimmer 2s infinite;
}

/* 图表容器 */
.chart-container {
    background: var(--bg-secondary);
    border-radius: 12px;
    padding: var(--space-6);
    margin-top: var(--space-6);
    animation: fadeIn 0.6s var(--ease-out) 0.2s both;
}

.chart-title {
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: var(--space-4);
    text-align: center;
}

/* 雷达图容器 */
.radar-chart {
    display: flex;
    justify-content: center;
    align-items: center;
    margin: var(--space-6) 0;
    animation: fadeIn 0.6s var(--ease-out) 0.3s both;
}

/* 技能网格 */
.skills-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--space-6);
    margin-top: var(--space-6);
}

.skill-card {
    background: var(--bg-secondary);
    border-radius: 12px;
    padding: var(--space-6);
    text-align: center;
    transition: all 0.3s var(--ease-out);
    animation: slideUp 0.5s var(--ease-out) 0.4s both;
}

.skill-card:hover {
    transform: translateY(-4px);
    box-shadow: var(--shadow-md);
}

.skill-icon {
    font-size: 2.5rem;
    margin-bottom: var(--space-4);
}

.skill-card h4 {
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: var(--space-3);
}

.skill-level {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.level-bar {
    flex: 1;
    height: 6px;
    background: var(--bg-tertiary);
    border-radius: 3px;
    overflow: hidden;
}

.level-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--primary), var(--secondary));
    border-radius: 3px;
    transition: width 1s var(--ease-in-out);
}

/* 书架设计 */
.book-shelf {
    display: flex;
    gap: var(--space-4);
    margin: var(--space-6) 0;
    animation: fadeIn 0.6s var(--ease-out) 0.5s both;
}

.book {
    width: 60px;
    height: 200px;
    position: relative;
    transform-style: preserve-3d;
    transform: rotateX(10deg);
    transition: transform 0.3s var(--ease-out);
    cursor: pointer;
}

.book:hover {
    transform: rotateX(10deg) translateY(-10px);
}

.book-spine {
    position: absolute;
    width: 100%;
    height: 100%;
    background: linear-gradient(to right, var(--color), color-mix(in srgb, var(--color) 80%, black));
    border-radius: 4px 0 0 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    font-weight: bold;
    writing-mode: vertical-rl;
    text-orientation: mixed;
    transform: rotateY(0deg) translateZ(-2px);
}

.book-cover {
    position: absolute;
    width: 100%;
    height: 100%;
    background: var(--color);
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    font-weight: bold;
    transform: rotateY(90deg) translateX(2px);
    transform-origin: left;
}

/* 统计卡片 */
.stats-cards {
    display: flex;
    gap: var(--space-6);
    margin-top: var(--space-6);
    animation: fadeIn 0.6s var(--ease-out) 0.6s both;
}

.stat-card {
    flex: 1;
    background: var(--bg-secondary);
    border-radius: 12px;
    padding: var(--space-6);
    text-align: center;
    transition: all 0.3s var(--ease-out);
}

.stat-card:hover {
    transform: translateY(-4px);
    box-shadow: var(--shadow-md);
}

.stat-card .stat-number {
    font-size: 2.5rem;
    font-weight: 900;
    background: linear-gradient(135deg, var(--primary), var(--secondary));
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    margin-bottom: var(--space-2);
}

.stat-card .stat-label {
    font-size: 0.9rem;
    color: var(--text-tertiary);
    text-transform: uppercase;
    letter-spacing: 1px;
}

/* 平衡轮 */
.balance-wheel {
    width: 300px;
    height: 300px;
    position: relative;
    margin: var(--space-8) auto;
    animation: rotateWheel 20s infinite linear;
}

.wheel-item {
    position: absolute;
    width: 100px;
    height: 100px;
    background: var(--primary);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    font-weight: 600;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
    transition: all 0.3s var(--ease-out);
    animation: pulse 2s infinite var(--ease-in-out);
}

.wheel-item:hover {
    transform: scale(1.1);
    z-index: 10;
}

.wheel-item:nth-child(1) {
    top: calc(50% - 50px);
    left: calc(50% - 50px);
    transform: rotate(0deg) translate(150px) rotate(0deg);
    background: linear-gradient(135deg, var(--primary), var(--primary-dark));
}

.wheel-item:nth-child(2) {
    top: calc(50% - 50px);
    left: calc(50% - 50px);
    transform: rotate(120deg) translate(150px) rotate(-120deg);
    background: linear-gradient(135deg, var(--secondary), var(--secondary-dark));
}

.wheel-item:nth-child(3) {
    top: calc(50% - 50px);
    left: calc(50% - 50px);
    transform: rotate(240deg) translate(150px) rotate(-240deg);
    background: linear-gradient(135deg, var(--accent), var(--accent-dark));
}

/* 习惯追踪 */
.habit-tracker {
    background: var(--bg-secondary);
    border-radius: 12px;
    padding: var(--space-6);
    margin-top: var(--space-6);
    animation: fadeIn 0.6s var(--ease-out) 0.7s both;
}

.habit {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--space-4);
    padding-bottom: var(--space-4);
    border-bottom: 1px solid var(--border-color);
}

.habit:last-child {
    margin-bottom: 0;
    padding-bottom: 0;
    border-bottom: none;
}

.habit span {
    font-weight: 600;
    color: var(--text-primary);
}

.habit-dots {
    display: flex;
    gap: var(--space-2);
}

.dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--bg-tertiary);
    transition: all 0.3s var(--ease-out);
}

.dot.active {
    background: var(--success);
    box-shadow: 0 0 0 2px rgba(81, 207, 102, 0.2);
}

.dot:hover {
    transform: scale(1.2);
}

/* 目标列表 */
.goal-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    margin-top: var(--space-6);
    animation: fadeIn 0.6s var(--ease-out) 0.8s both;
}

.goal-item {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-4);
    background: var(--bg-secondary);
    border-radius: 8px;
    transition: all 0.3s var(--ease-out);
}

.goal-item:hover {
    transform: translateX(4px);
    box-shadow: var(--shadow-sm);
}

.goal-checkbox {
    width: 24px;
    height: 24px;
    border: 2px solid var(--border-color);
    border-radius: 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: all 0.3s var(--ease-out);
}

.goal-checkbox:hover {
    border-color: var(--primary);
}

.goal-content h3 {
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: var(--space-1);
}

.goal-content p {
    font-size: 0.9rem;
    color: var(--text-secondary);
}

/* 路线图 */
.roadmap {
    display: flex;
    justify-content: space-between;
    margin-top: var(--space-8);
    position: relative;
    animation: fadeIn 0.6s var(--ease-out) 0.9s both;
}

.roadmap::before {
    content: '';
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    height: 2px;
    background: linear-gradient(to right, var(--primary), var(--secondary));
    transform: translateY(-50%);
    z-index: 1;
}

.roadmap-item {
    position: relative;
    z-index: 2;
    width: 120px;
    height: 120px;
    background: white;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 600;
    color: var(--primary);
    border: 4px solid var(--primary);
    box-shadow: var(--shadow-md);
    transition: all 0.3s var(--ease-out);
}

.roadmap-item:hover {
    transform: scale(1.1);
    box-shadow: var(--shadow-lg);
}

/* 导航控制 */
.navigation {
    position: fixed;
    bottom: var(--space-8);
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: var(--space-2);
    background: rgba(255, 255, 255, 0.9);
    backdrop-filter: blur(10px);
    padding: var(--space-3) var(--space-4);
    border-radius: 50px;
    box-shadow: var(--shadow-lg);
    z-index: 1000;
    animation: slideUp 0.6s var(--ease-out) 1s both;
}

.nav-btn {
    width: 44px;
    height: 44px;
    border: none;
    background: linear-gradient(135deg, var(--primary), var(--primary-dark));
    color: white;
    border-radius: 50%;
    font-size: 1.2rem;
    cursor: pointer;
    transition: all 0.3s var(--ease-out);
    display: flex;
    align-items: center;
    justify-content: center;
}

.nav-btn:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
}

.nav-btn:active {
    transform: translateY(0);
}

/* 进度指示器 */
.progress-indicator {
    position: fixed;
    right: var(--space-8);
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    z-index: 1000;
    animation: fadeIn 0.6s var(--ease-out) 1.1s both;
}

.progress-dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.3);
    cursor: pointer;
    transition: all 0.3s var(--ease-out);
    position: relative;
}

.progress-dot:hover {
    transform: scale(1.2);
    background: rgba(255, 255, 255, 0.6);
}

.progress-dot.active {
    background: white;
    box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.2);
    animation: pulse 2s infinite var(--ease-in-out);
}

.progress-dot::after {
    content: attr(data-step);
    position: absolute;
    right: 20px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 0.8rem;
    color: white;
    opacity: 0;
    transition: opacity 0.3s var(--ease-out);
    pointer-events: none;
    white-space: nowrap;
}

.progress-dot:hover::after {
    opacity: 1;
}

/* 动画关键帧 */
@keyframes rotateCube {
    0% { transform: rotateX(0) rotateY(0) rotateZ(0); }
    33% { transform: rotateX(120deg) rotateY(120deg) rotateZ(0); }
    66% { transform: rotateX(240deg) rotateY(240deg) rotateZ(120deg); }
    100% { transform: rotateX(360deg) rotateY(360deg) rotateZ(360deg); }
}

@keyframes floatCube {
    0%, 100% { transform: rotateX(0) rotateY(0) translateZ(0); }
    25% { transform: rotateX(10deg) rotateY(90deg) translateZ(-20px); }
    50% { transform: rotateX(0) rotateY(180deg) translateZ(0); }
    75% { transform: rotateX(-10deg) rotateY(270deg) translateZ(-20px); }
}

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

@keyframes fadeInUp {
    from { 
        opacity: 0;
        transform: translateY(20px);
    }
    to { 
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes slideInRight {
    from { 
        opacity: 0;
        transform: translateX(20px);
    }
    to { 
        opacity: 1;
        transform: translateX(0);
    }
}

@keyframes slideUp {
    from { 
        opacity: 0;
        transform: translateY(20px);
    }
    to { 
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes rotateWheel {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

@keyframes pulse {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.05); }
}

@keyframes shimmer {
    0% { transform: translateX(-100%); }
    100% { transform: translateX(100%); }
}

/* 响应式设计 */
@media (max-width: 1024px) {
    .step {
        width: 90vw;
        height: 80vh;
    }
    
    .cover-title {
        font-size: 3rem;
    }
    
    .cover-stats {
        flex-direction: column;
        align-items: center;
    }
    
    .cube-frame {
        width: 200px;
        height: 200px;
    }
    
    .cube-face {
        width: 200px;
        height: 200px;
        font-size: 2rem;
    }
    
    .cube-face.front { transform: translateZ(100px); }
    .cube-face.back { transform: translateZ(-100px) rotateY(180deg); }
    .cube-face.right { transform: translateX(100px) rotateY(90deg); }
    .cube-face.left { transform: translateX(-100px) rotateY(-90deg); }
    .cube-face.top { transform: translateY(-100px) rotateX(90deg); }
    .cube-face.bottom { transform: translateY(100px) rotateX(-90deg); }
}

@media (max-width: 768px) {
    html { font-size: 14px; }
    
    .step {
        width: 95vw;
        height: 85vh;
        padding: var(--space-4);
    }
    
    .cover-title {
        font-size: 2.5rem;
    }
    
    .navigation {
        flex-wrap: wrap;
        justify-content: center;
        width: 90%;
        padding: var(--space-2);
    }
    
    .nav-btn {
        width: 36px;
        height: 36px;
        font-size: 1rem;
    }
    
    .progress-indicator {
        right: var(--space-4);
    }
}

@media (max-width: 480px) {
    html { font-size: 12px; }
    
    .cover-title {
        font-size: 2rem;
    }
    
    .section-title {
        font-size: 2rem;
    }
    
    .skills-grid {
        grid-template-columns: 1fr;
    }
    
    .stats-cards {
        flex-direction: column;
    }
}

2.3 JavaScript逻辑与交互

javascript 复制代码
// 主应用控制器
class YearReviewApp {
    constructor() {
        this.currentStep = 0;
        this.totalSteps = 6;
        this.stepNames = ['cover', 'achievements', 'skills', 'learning', 'life', 'future'];
        this.impressApi = null;
        this.animationFrame = null;
        this.isAnimating = false;
        this.data = this.loadData();
        
        this.init();
    }
    
    // 加载数据
    loadData() {
        return {
            achievements: [
                { quarter: 'Q1', project: 'Alpha', progress: 85, description: '系统重构,性能提升40%' },
                { quarter: 'Q2', project: 'Beta', progress: 92, description: '新功能,用户增长200%' },
                { quarter: 'Q3', project: 'Gamma', progress: 78, description: '国际化版本发布' },
                { quarter: 'Q4', project: 'Delta', progress: 65, description: 'AI集成项目' }
            ],
            skills: {
                labels: ['前端', '后端', '设计', '数据', '沟通', '管理'],
                data: [85, 70, 75, 60, 80, 65]
            },
            habits: {
                exercise: Array(30).fill(0).map((_, i) => i % 3 !== 0),
                reading: Array(30).fill(0).map((_, i) => i % 2 !== 0),
                learning: Array(30).fill(0).map((_, i) => i % 4 !== 0)
            },
            goals: [
                { id: 1, title: '技术深度', description: '深入研究Web3与AI结合', completed: false },
                { id: 2, title: '开源贡献', description: '提交至少3个有意义的PR', completed: false },
                { id: 3, title: '健康管理', description: '每周运动3次以上', completed: false },
                { id: 4, title: '个人品牌', description: '发布12篇技术文章', completed: false }
            ]
        };
    }
    
    // 初始化应用
    init() {
        console.log('初始化年度总结应用...');
        
        // 绑定事件
        this.bindEvents();
        
        // 初始化Impress.js
        this.initImpress();
        
        // 加载数据
        this.loadStats();
        
        // 初始化图表
        this.initCharts();
        
        // 启动动画
        this.startAnimations();
        
        // 隐藏加载屏幕
        setTimeout(() => {
            this.hideLoadingScreen();
        }, 1500);
    }
    
    // 绑定事件
    bindEvents() {
        // 进度指示器点击
        document.querySelectorAll('.progress-dot').forEach((dot, index) => {
            dot.addEventListener('click', () => {
                this.gotoStep(index);
            });
        });
        
        // 目标复选框
        document.querySelectorAll('.goal-checkbox').forEach((checkbox, index) => {
            checkbox.addEventListener('click', () => {
                this.toggleGoal(index);
            });
        });
        
        // 窗口大小变化
        window.addEventListener('resize', () => {
            this.handleResize();
        });
        
        // 鼠标滚轮控制
        this.setupWheelControl();
        
        // 触摸控制
        this.setupTouchControl();
    }
    
    // 初始化Impress.js
    initImpress() {
        try {
            if (typeof impress === 'function') {
                this.impressApi = impress();
                this.impressApi.init();
                
                // 监听步骤切换
                document.addEventListener('impress:stepenter', (e) => {
                    this.handleStepEnter(e.target.id);
                });
                
                // 监听初始化完成
                document.addEventListener('impress:init', () => {
                    console.log('Impress.js 初始化完成');
                });
            } else {
                throw new Error('Impress.js 未正确加载');
            }
        } catch (error) {
            console.error('Impress.js 初始化失败:', error);
            this.showError('演示引擎初始化失败,请刷新页面重试');
        }
    }
    
    // 处理步骤进入
    handleStepEnter(stepId) {
        this.currentStep = this.stepNames.indexOf(stepId);
        
        // 更新进度指示器
        this.updateProgressIndicator();
        
        // 根据步骤触发特定动画
        switch(stepId) {
            case 'achievements':
                this.animateAchievements();
                break;
            case 'skills':
                this.animateSkills();
                break;
            case 'learning':
                this.animateLearning();
                break;
            case 'life':
                this.animateLife();
                break;
            case 'future':
                this.animateFuture();
                break;
        }
        
        // 更新URL
        window.location.hash = `#/${stepId}`;
    }
    
    // 更新进度指示器
    updateProgressIndicator() {
        document.querySelectorAll('.progress-dot').forEach((dot, index) => {
            dot.classList.toggle('active', index === this.currentStep);
        });
    }
    
    // 跳转到指定步骤
    gotoStep(step) {
        if (typeof step === 'number') {
            step = this.stepNames[step];
        }
        
        if (this.impressApi && this.impressApi.goto) {
            this.impressApi.goto(step);
        }
    }
    
    // 旋转立方体
    rotateCube(direction) {
        if (this.isAnimating) return;
        
        this.isAnimating = true;
        const currentIndex = this.currentStep;
        let targetIndex;
        
        if (direction === 'next') {
            targetIndex = (currentIndex + 1) % this.totalSteps;
        } else {
            targetIndex = (currentIndex - 1 + this.totalSteps) % this.totalSteps;
        }
        
        this.gotoStep(targetIndex);
        
        // 添加旋转动画效果
        this.animateCubeRotation(direction);
        
        setTimeout(() => {
            this.isAnimating = false;
        }, 1000);
    }
    
    // 动画立方体旋转
    animateCubeRotation(direction) {
        const cube = document.querySelector('#cube-animation');
        if (!cube) return;
        
        const currentRotation = this.getRotationValues(cube.style.transform);
        const angle = direction === 'next' ? 90 : -90;
        
        // 根据当前步骤决定旋转轴
        let axis, rotation;
        
        switch(this.currentStep) {
            case 0: // 封面 -> 成就
            case 1: // 成就 -> 技能
                axis = 'Y';
                rotation = direction === 'next' ? -90 : 90;
                break;
            case 2: // 技能 -> 学习
                axis = 'X';
                rotation = direction === 'next' ? 90 : -90;
                break;
            case 3: // 学习 -> 生活
                axis = 'Y';
                rotation = direction === 'next' ? 90 : -90;
                break;
            case 4: // 生活 -> 未来
                axis = 'X';
                rotation = direction === 'next' ? -90 : 90;
                break;
            case 5: // 未来 -> 封面
                axis = 'Y';
                rotation = direction === 'next' ? 180 : -180;
                break;
        }
        
        cube.style.transition = 'transform 1s cubic-bezier(0.175, 0.885, 0.32, 1.275)';
        cube.style.transform = `rotate${axis}(${currentRotation[axis] + rotation}deg)`;
    }
    
    // 获取旋转值
    getRotationValues(transform) {
        const values = { X: 0, Y: 0, Z: 0 };
        
        if (!transform) return values;
        
        const matches = transform.match(/rotateX\(([-\d.]+)deg\).*rotateY\(([-\d.]+)deg\).*rotateZ\(([-\d.]+)deg\)/);
        if (matches) {
            values.X = parseFloat(matches[1]) || 0;
            values.Y = parseFloat(matches[2]) || 0;
            values.Z = parseFloat(matches[3]) || 0;
        }
        
        return values;
    }
    
    // 加载统计数据
    loadStats() {
        // 天数统计
        const startDate = new Date('2024-01-01');
        const endDate = new Date('2024-12-31');
        const daysCount = Math.ceil((endDate - startDate) / (1000 * 60 * 60 * 24));
        
        // 项目数量
        const projectsCount = this.data.achievements.length;
        
        // 成长百分比
        const totalProgress = this.data.achievements.reduce((sum, item) => sum + item.progress, 0);
        const avgProgress = Math.round(totalProgress / this.data.achievements.length);
        
        // 动画更新数字
        this.animateNumber('daysCount', 0, daysCount, 2000);
        this.animateNumber('projectsCount', 0, projectsCount, 1500);
        this.animateNumber('growthCount', 0, avgProgress, 2500);
    }
    
    // 数字动画
    animateNumber(elementId, start, end, duration) {
        const element = document.getElementById(elementId);
        if (!element) return;
        
        const startTime = performance.now();
        const step = (timestamp) => {
            const progress = Math.min((timestamp - startTime) / duration, 1);
            const current = Math.floor(start + progress * (end - start));
            
            element.textContent = elementId === 'growthCount' ? `${current}%` : current;
            
            if (progress < 1) {
                this.animationFrame = requestAnimationFrame(step);
            }
        };
        
        this.animationFrame = requestAnimationFrame(step);
    }
    
    // 初始化图表
    initCharts() {
        this.initPerformanceChart();
        this.initSkillsChart();
    }
    
    // 初始化绩效图表
    initPerformanceChart() {
        const canvas = document.getElementById('performanceChart');
        if (!canvas) return;
        
        const ctx = canvas.getContext('2d');
        const data = this.data.achievements;
        
        // 清除画布
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        
        // 绘制网格
        ctx.strokeStyle = 'rgba(0, 0, 0, 0.1)';
        ctx.lineWidth = 1;
        
        // 水平网格线
        for (let i = 0; i <= 100; i += 20) {
            const y = canvas.height - (i / 100) * canvas.height;
            ctx.beginPath();
            ctx.moveTo(0, y);
            ctx.lineTo(canvas.width, y);
            ctx.stroke();
            
            // 标签
            ctx.fillStyle = '#666';
            ctx.font = '12px Arial';
            ctx.fillText(`${i}%`, 5, y - 5);
        }
        
        // 计算柱状图参数
        const barWidth = 40;
        const spacing = 20;
        const startX = 60;
        
        data.forEach((item, index) => {
            const x = startX + index * (barWidth + spacing);
            const barHeight = (item.progress / 100) * (canvas.height - 40);
            const y = canvas.height - barHeight;
            
            // 创建渐变
            const gradient = ctx.createLinearGradient(x, y, x, canvas.height);
            gradient.addColorStop(0, '#667eea');
            gradient.addColorStop(1, '#764ba2');
            
            // 绘制柱状
            ctx.fillStyle = gradient;
            ctx.fillRect(x, y, barWidth, barHeight);
            
            // 圆角顶部
            ctx.beginPath();
            ctx.moveTo(x, y + 5);
            ctx.quadraticCurveTo(x, y, x + 5, y);
            ctx.lineTo(x + barWidth - 5, y);
            ctx.quadraticCurveTo(x + barWidth, y, x + barWidth, y + 5);
            ctx.fill();
            
            // 标签
            ctx.fillStyle = '#2d3436';
            ctx.font = 'bold 14px Arial';
            ctx.textAlign = 'center';
            ctx.fillText(item.quarter, x + barWidth / 2, canvas.height - 5);
            
            // 数值
            ctx.fillStyle = '#fff';
            ctx.fillText(`${item.progress}%`, x + barWidth / 2, y - 10);
        });
    }
    
    // 初始化技能雷达图
    initSkillsChart() {
        const canvas = document.getElementById('skillsChart');
        if (!canvas) return;
        
        const ctx = canvas.getContext('2d');
        const centerX = canvas.width / 2;
        const centerY = canvas.height / 2;
        const radius = Math.min(centerX, centerY) - 20;
        const sides = this.data.skills.labels.length;
        const angleStep = (Math.PI * 2) / sides;
        
        // 清除画布
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        
        // 绘制网格
        ctx.strokeStyle = 'rgba(0, 0, 0, 0.1)';
        ctx.lineWidth = 1;
        
        for (let i = 1; i <= 5; i++) {
            ctx.beginPath();
            const r = (radius / 5) * i;
            
            for (let j = 0; j <= sides; j++) {
                const angle = j * angleStep - Math.PI / 2;
                const x = centerX + Math.cos(angle) * r;
                const y = centerY + Math.sin(angle) * r;
                
                if (j === 0) {
                    ctx.moveTo(x, y);
                } else {
                    ctx.lineTo(x, y);
                }
            }
            
            ctx.closePath();
            ctx.stroke();
        }
        
        // 绘制轴线
        ctx.strokeStyle = 'rgba(0, 0, 0, 0.2)';
        ctx.lineWidth = 1;
        
        for (let i = 0; i < sides; i++) {
            const angle = i * angleStep - Math.PI / 2;
            const x = centerX + Math.cos(angle) * radius;
            const y = centerY + Math.sin(angle) * radius;
            
            ctx.beginPath();
            ctx.moveTo(centerX, centerY);
            ctx.lineTo(x, y);
            ctx.stroke();
        }
        
        // 绘制数据多边形
        ctx.beginPath();
        for (let i = 0; i < sides; i++) {
            const angle = i * angleStep - Math.PI / 2;
            const value = this.data.skills.data[i] / 100;
            const r = radius * value;
            const x = centerX + Math.cos(angle) * r;
            const y = centerY + Math.sin(angle) * r;
            
            if (i === 0) {
                ctx.moveTo(x, y);
            } else {
                ctx.lineTo(x, y);
            }
        }
        
        ctx.closePath();
        ctx.fillStyle = 'rgba(102, 126, 234, 0.3)';
        ctx.strokeStyle = '#667eea';
        ctx.lineWidth = 2;
        ctx.fill();
        ctx.stroke();
        
        // 绘制数据点
        for (let i = 0; i < sides; i++) {
            const angle = i * angleStep - Math.PI / 2;
            const value = this.data.skills.data[i] / 100;
            const r = radius * value;
            const x = centerX + Math.cos(angle) * r;
            const y = centerY + Math.sin(angle) * r;
            
            // 点
            ctx.beginPath();
            ctx.arc(x, y, 4, 0, Math.PI * 2);
            ctx.fillStyle = '#fff';
            ctx.fill();
            ctx.strokeStyle = '#667eea';
            ctx.lineWidth = 2;
            ctx.stroke();
            
            // 标签
            const labelX = centerX + Math.cos(angle) * (radius + 20);
            const labelY = centerY + Math.sin(angle) * (radius + 20);
            
            ctx.fillStyle = '#2d3436';
            ctx.font = '12px Arial';
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            ctx.fillText(this.data.skills.labels[i], labelX, labelY);
        }
    }
    
    // 动画效果
    startAnimations() {
        this.animateCube();
        this.animateProgressBars();
        this.animateHabitDots();
    }
    
    // 立方体动画
    animateCube() {
        const cube = document.querySelector('#cube-animation');
        if (!cube) return;
        
        let rotation = 0;
        const animate = () => {
            rotation += 0.2;
            cube.style.transform = `rotateX(${Math.sinrotation / 10) * 5}deg) rotateY(${rotation}deg)`;
            requestAnimationFrame(animate);
        };
        animate();
    }
    
    // 动画进度条
    animateProgressBars() {
        document.querySelectorAll('.progress-fill, .level-fill').forEach(fill => {
            const targetWidth = fill.style.width;
            fill.style.width = '0';
            setTimeout(() => {
                fill.style.width = targetWidth;
            }, 500);
        });
    }
    
    // 动画习惯点
    animateHabitDots() {
        document.querySelectorAll('.dot.active').forEach((dot, index) => {
            setTimeout(() => {
                dot.style.transform = 'scale(1.2)';
                setTimeout(() => {
                    dot.style.transform = 'scale(1)';
                }, 300);
            }, index * 100);
        });
    }
    
    // 动画成就页面
    animateAchievements() {
        const timelineItems = document.querySelectorAll('.timeline-item');
        timelineItems.forEach((item, index) => {
            setTimeout(() => {
                item.style.opacity = '1';
                item.style.transform = 'translateX(0)';
            }, index * 200);
        });
    }
    
    // 动画技能页面
    animateSkills() {
        const skillCards = document.querySelectorAll('.skill-card');
        skillCards.forEach((card, index) => {
            setTimeout(() => {
                card.style.opacity = '1';
                card.style.transform = 'translateY(0)';
            }, index * 150);
        });
    }
    
    // 动画学习页面
    animateLearning() {
        const books = document.querySelectorAll('.book');
        books.forEach((book, index) => {
            setTimeout(() => {
                book.style.opacity = '1';
                book.style.transform = 'rotateX(10deg)';
            }, index * 200);
        });
    }
    
    // 动画生活页面
    animateLife() {
        const wheelItems = document.querySelectorAll('.wheel-item');
        wheelItems.forEach((item, index) => {
            setTimeout(() => {
                item.style.opacity = '1';
                item.style.transform = item.style.transform.replace('scale(0)', 'scale(1)');
            }, index * 200);
        });
    }
    
    // 动画未来页面
    animateFuture() {
        const goalItems = document.querySelectorAll('.goal-item');
        goalItems.forEach((item, index) => {
            setTimeout(() => {
                item.style.opacity = '1';
                item.style.transform = 'translateX(0)';
            }, index * 150);
        });
    }
    
    // 切换目标完成状态
    toggleGoal(index) {
        const goal = this.data.goals[index];
        goal.completed = !goal.completed;
        
        const checkbox = document.querySelectorAll('.goal-checkbox')[index];
        if (goal.completed) {
            checkbox.innerHTML = '✓';
            checkbox.style.background = 'var(--success)';
            checkbox.style.borderColor = 'var(--success)';
            checkbox.style.color = 'white';
        } else {
            checkbox.innerHTML = '';
            checkbox.style.background = 'transparent';
            checkbox.style.borderColor = 'var(--border-color)';
        }
    }
    
    // 设置滚轮控制
    setupWheelControl() {
        let wheelTimeout;
        document.addEventListener('wheel', (e) => {
            e.preventDefault();
            
            if (wheelTimeout) clearTimeout(wheelTimeout);
            
            wheelTimeout = setTimeout(() => {
                if (e.deltaY > 0) {
                    this.rotateCube('next');
                } else {
                    this.rotateCube('prev');
                }
            }, 100);
        }, { passive: false });
    }
    
    // 设置触摸控制
    setupTouchControl() {
        let startX, startY;
        const threshold = 50;
        
        document.addEventListener('touchstart', (e) => {
            startX = e.touches[0].clientX;
            startY = e.touches[0].clientY;
        }, { passive: true });
        
        document.addEventListener('touchend', (e) => {
            if (!startX || !startY) return;
            
            const endX = e.changedTouches[0].clientX;
            const endY = e.changedTouches[0].clientY;
            const diffX = endX - startX;
            const diffY = endY - startY;
            
            // 水平滑动优先
            if (Math.abs(diffX) > Math.abs(diffY) && Math.abs(diffX) > threshold) {
                if (diffX > 0) {
                    this.rotateCube('prev');
                } else {
                    this.rotateCube('next');
                }
            }
            
            startX = null;
            startY = null;
        }, { passive: true });
    }
    
    // 处理窗口大小变化
    handleResize() {
        // 重新初始化图表
        this.initCharts();
        
        // 重新计算布局
        this.updateLayout();
    }
    
    // 更新布局
    updateLayout() {
        // 可以根据窗口大小调整布局
        const isMobile = window.innerWidth < 768;
        
        if (isMobile) {
            document.body.classList.add('mobile');
        } else {
            document.body.classList.remove('mobile');
        }
    }
    
    // 隐藏加载屏幕
    hideLoadingScreen() {
        const loadingScreen = document.getElementById('loadingScreen');
        if (loadingScreen) {
            loadingScreen.style.opacity = '0';
            setTimeout(() => {
                loadingScreen.style.display = 'none';
            }, 800);
        }
    }
    
    // 显示错误
    showError(message) {
        const errorDiv = document.createElement('div');
        errorDiv.className = 'error-message';
        errorDiv.innerHTML = `
            <div class="error-content">
                <span class="error-icon">⚠️</span>
                <p>${message}</p>
                <button onclick="location.reload()">重新加载</button>
            </div>
        `;
        
        document.body.appendChild(errorDiv);
        
        // 3秒后自动移除
        setTimeout(() => {
            if (errorDiv.parentNode) {
                errorDiv.parentNode.removeChild(errorDiv);
            }
        }, 3000);
    }
}

// 初始化应用
let app;

document.addEventListener('DOMContentLoaded', () => {
    console.log('DOM加载完成,初始化应用...');
    app = new YearReviewApp();
});

// 全局函数,供按钮调用
function rotateCube(direction) {
    if (app) {
        app.rotateCube(direction);
    }
}

function gotoStep(step) {
    if (app) {
        app.gotoStep(step);
    }
}

// 添加错误处理
window.addEventListener('error', (e) => {
    console.error('全局错误:', e.error);
    
    if (app) {
        app.showError(`发生错误: ${e.message}`);
    }
});

// 添加页面可见性监听
document.addEventListener('visibilitychange', () => {
    if (!document.hidden && app) {
        // 页面重新可见时,重新初始化图表
        app.initCharts();
    }
});

// 添加样式
const errorStyle = document.createElement('style');
errorStyle.textContent = `
    .error-message {
        position: fixed;
        top: 20px;
        right: 20px;
        background: linear-gradient(135deg, #ff6b6b, #ee5a5a);
        color: white;
        padding: 1rem 1.5rem;
        border-radius: 8px;
        box-shadow: 0 4px 12px rgba(0,0,0,0.2);
        z-index: 9999;
        animation: slideInRight 0.3s ease-out;
    }
    
    .error-content {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 0.5rem;
    }
    
    .error-icon {
        font-size: 1.5rem;
    }
    
    .error-message p {
        margin: 0;
        font-size: 0.9rem;
    }
    
    .error-message button {
        background: white;
        color: #ff6b6b;
        border: none;
        padding: 0.25rem 0.75rem;
        border-radius: 4px;
        cursor: pointer;
        font-weight: 600;
        transition: all 0.2s;
    }
    
    .error-message button:hover {
        background: rgba(255,255,255,0.9);
    }
    
    @keyframes slideInRight {
        from {
            transform: translateX(100%);
            opacity: 0;
        }
        to {
            transform: translateX(0);
            opacity: 1;
        }
    }
`;

document.head.appendChild(errorStyle);

3. 设计要点与展示效果规划

3.1 立方体导航系统设计

3.2 动画时序规划

复制代码

3.3 响应式断点设计

css 复制代码
/* 响应式断点系统 */
:root {
    /* 断点定义 */
    --breakpoint-xs: 0;
    --breakpoint-sm: 576px;
    --breakpoint-md: 768px;
    --breakpoint-lg: 992px;
    --breakpoint-xl: 1200px;
    --breakpoint-xxl: 1400px;
    
    /* 容器最大宽度 */
    --container-sm: 540px;
    --container-md: 720px;
    --container-lg: 960px;
    --container-xl: 1140px;
    --container-xxl: 1320px;
}

4. 分析与设计

4.1 系统架构设计

4.2 组件设计模式

javascript 复制代码
// 组件基类
class BaseComponent {
    constructor(element) {
        this.element = element;
        this.init();
    }
    
    init() {
        // 初始化逻辑
    }
    
    show() {
        this.element.style.opacity = '1';
        this.element.style.visibility = 'visible';
    }
    
    hide() {
        this.element.style.opacity = '0';
        this.element.style.visibility = 'hidden';
    }
    
    on(event, handler) {
        this.element.addEventListener(event, handler);
    }
}

// 立方体组件
class CubeComponent extends BaseComponent {
    constructor(element) {
        super(element);
        this.rotation = { x: 0, y: 0, z: 0 };
        this.isAnimating = false;
    }
    
    rotateTo(face) {
        if (this.isAnimating) return;
        
        this.isAnimating = true;
        
        const rotations = {
            front: { x: 0, y: 0 },
            back: { x: 0, y: 180 },
            right: { x: 0, y: 90 },
            left: { x: 0, y: -90 },
            top: { x: 90, y: 0 },
            bottom: { x: -90, y: 0 }
        };
        
        const target = rotations[face] || rotations.front;
        
        this.animateRotation(target.x, target.y, () => {
            this.isAnimating = false;
        });
    }
    
    animateRotation(x, y, callback) {
        const duration = 1000;
        const startTime = performance.now();
        const startX = this.rotation.x;
        const startY = this.rotation.y;
        
        const animate = (currentTime) => {
            const elapsed = currentTime - startTime;
            const progress = Math.min(elapsed / duration, 1);
            const easeProgress = this.easeInOutCubic(progress);
            
            this.rotation.x = startX + (x - startX) * easeProgress;
            this.rotation.y = startY + (y - startY) * easeProgress;
            
            this.element.style.transform = 
                `rotateX(${this.rotation.x}deg) rotateY(${this.rotation.y}deg)`;
            
            if (progress < 1) {
                requestAnimationFrame(animate);
            } else if (callback) {
                callback();
            }
        };
        
        requestAnimationFrame(animate);
    }
    
    easeInOutCubic(t) {
        return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
    }
}

4.3 状态管理设计

javascript 复制代码
// 状态管理器
class StateManager {
    constructor(initialState = {}) {
        this.state = initialState;
        this.listeners = new Map();
        this.history = [];
        this.maxHistory = 50;
    }
    
    // 获取状态
    getState(path) {
        if (!path) return this.state;
        
        return path.split('.').reduce((obj, key) => obj?.[key], this.state);
    }
    
    // 设置状态
    setState(path, value) {
        const oldState = JSON.parse(JSON.stringify(this.state));
        
        if (typeof path === 'object') {
            this.state = { ...this.state, ...path };
        } else {
            this.setNestedState(this.state, path, value);
        }
        
        // 保存到历史记录
        this.history.push({
            timestamp: Date.now(),
            state: oldState
        });
        
        // 限制历史记录数量
        if (this.history.length > this.maxHistory) {
            this.history.shift();
        }
        
        // 通知监听器
        this.notifyListeners(path, value);
    }
    
    // 设置嵌套状态
    setNestedState(obj, path, value) {
        const keys = path.split('.');
        const lastKey = keys.pop();
        const target = keys.reduce((o, key) => o[key] = o[key] || {}, obj);
        target[lastKey] = value;
    }
    
    // 订阅状态变化
    subscribe(path, listener) {
        if (!this.listeners.has(path)) {
            this.listeners.set(path, new Set());
        }
        this.listeners.get(path).add(listener);
        
        // 返回取消订阅函数
        return () => this.unsubscribe(path, listener);
    }
    
    // 取消订阅
    unsubscribe(path, listener) {
        if (this.listeners.has(path)) {
            this.listeners.get(path).delete(listener);
        }
    }
    
    // 通知监听器
    notifyListeners(path, value) {
        if (this.listeners.has(path)) {
            this.listeners.get(path).forEach(listener => {
                try {
                    listener(value, this.state);
                } catch (error) {
                    console.error(`监听器错误 (${path}):`, error);
                }
            });
        }
        
        // 通知通配符监听器
        if (this.listeners.has('*')) {
            this.listeners.get('*').forEach(listener => {
                try {
                    listener(path, value, this.state);
                } catch (error) {
                    console.error(`通配符监听器错误:`, error);
                }
            });
        }
    }
    
    // 撤销
    undo() {
        if (this.history.length > 0) {
            const previous = this.history.pop();
            this.state = previous.state;
            
            // 通知所有监听器
            this.notifyListeners('*', null);
        }
    }
}

5. 性能优化策略

5.1 渲染优化

javascript 复制代码
// 性能优化管理器
class PerformanceManager {
    constructor() {
        this.metrics = {
            fps: 0,
            frameTime: 0,
            memory: 0,
            paintCount: 0
        };
        
        this.thresholds = {
            fpsWarning: 30,
            fpsCritical: 20,
            memoryWarning: 100, // MB
            frameTimeWarning: 33 // ms
        };
        
        this.init();
    }
    
    init() {
        this.startFPSCounter();
        this.startMemoryMonitor();
        this.setupPerformanceObserver();
        this.startAutoOptimization();
    }
    
    startFPSCounter() {
        let frameCount = 0;
        let lastTime = performance.now();
        
        const checkFrame = () => {
            frameCount++;
            const currentTime = performance.now();
            
            if (currentTime >= lastTime + 1000) {
                this.metrics.fps = Math.round(
                    (frameCount * 1000) / (currentTime - lastTime)
                );
                this.metrics.frameTime = 1000 / this.metrics.fps;
                
                this.checkPerformance();
                
                frameCount = 0;
                lastTime = currentTime;
            }
            
            requestAnimationFrame(checkFrame);
        };
        
        requestAnimationFrame(checkFrame);
    }
    
    startMemoryMonitor() {
        if (performance.memory) {
            setInterval(() => {
                this.metrics.memory = 
                    performance.memory.usedJSHeapSize / 1024 / 1024;
            }, 5000);
        }
    }
    
    setupPerformanceObserver() {
        if ('PerformanceObserver' in window) {
            // 监控布局偏移
            const layoutObserver = new PerformanceObserver((list) => {
                this.metrics.layoutShift = 
                    list.getEntries().reduce((sum, entry) => sum + entry.value, 0);
            });
            
            try {
                layoutObserver.observe({ entryTypes: ['layout-shift'] });
            } catch (e) {
                // 旧版本浏览器支持
            }
        }
    }
    
    startAutoOptimization() {
        setInterval(() => {
            this.optimizePerformance();
        }, 10000);
    }
    
    optimizePerformance() {
        if (this.metrics.fps < this.thresholds.fpsWarning) {
            this.reduceAnimations();
        }
        
        if (this.metrics.memory > this.thresholds.memoryWarning) {
            this.cleanupMemory();
        }
    }
    
    reduceAnimations() {
        // 减少动画复杂度
        document.querySelectorAll('.animated').forEach(el => {
            el.style.animationDuration = '2s';
        });
        
        // 减少3D变换复杂度
        const style = document.createElement('style');
        style.textContent = `
            .step:not(.active) {
                opacity: 0.5 !important;
            }
        `;
        document.head.appendChild(style);
    }
    
    cleanupMemory() {
        // 清理未使用的缓存
        if (window.gc) {
            window.gc();
        }
        
        // 清理离屏Canvas
        document.querySelectorAll('canvas').forEach(canvas => {
            if (!this.isElementInViewport(canvas)) {
                const context = canvas.getContext('2d');
                if (context) {
                    context.clearRect(0, 0, canvas.width, canvas.height);
                }
            }
        });
    }
    
    isElementInViewport(el) {
        const rect = el.getBoundingClientRect();
        return (
            rect.top >= 0 &&
            rect.left >= 0 &&
            rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
            rect.right <= (window.innerWidth || document.documentElement.clientWidth)
        );
    }
    
    getReport() {
        return {
            timestamp: Date.now(),
            metrics: { ...this.metrics },
            recommendations: this.getRecommendations()
        };
    }
    
    getRecommendations() {
        const recs = [];
        
        if (this.metrics.fps < this.thresholds.fpsCritical) {
            recs.push('考虑禁用复杂动画');
            recs.push('减少同时显示的3D元素数量');
        }
        
        if (this.metrics.memory > this.thresholds.memoryWarning) {
            recs.push('清理离屏Canvas资源');
            recs.push('考虑使用虚拟列表');
        }
        
        return recs;
    }
}

5.2 内存管理

javascript 复制代码
// 内存管理器
class MemoryManager {
    constructor() {
        this.cache = new Map();
        this.weakRefs = new WeakMap();
        this.init();
    }
    
    init() {
        this.setupCleanupInterval();
        this.setupMemoryWarning();
    }
    
    // 缓存资源
    cacheResource(key, resource, options = {}) {
        const cacheEntry = {
            data: resource,
            timestamp: Date.now(),
            ttl: options.ttl || 5 * 60 * 1000, // 5分钟
            size: this.estimateSize(resource),
            accessCount: 0
        };
        
        this.cache.set(key, cacheEntry);
        
        // 如果缓存超过限制,清理最旧的项目
        if (this.getTotalCacheSize() > 50 * 1024 * 1024) { // 50MB
            this.cleanupOldCacheItems();
        }
        
        return resource;
    }
    
    // 获取缓存资源
    getCachedResource(key) {
        const entry = this.cache.get(key);
        if (entry) {
            entry.accessCount++;
            entry.lastAccess = Date.now();
            
            // 检查是否过期
            if (Date.now() - entry.timestamp > entry.ttl) {
                this.cache.delete(key);
                return null;
            }
            
            return entry.data;
        }
        return null;
    }
    
    // 跟踪DOM元素引用
    trackElementReference(element, data) {
        if (!this.weakRefs.has(element)) {
            this.weakRefs.set(element, new Set());
        }
        this.weakRefs.get(element).add(data);
    }
    
    // 清理元素引用
    cleanupElementReferences(element) {
        this.weakRefs.delete(element);
    }
    
    // 自动清理
    setupCleanupInterval() {
        setInterval(() => {
            this.cleanupExpiredCache();
            this.cleanupUnusedResources();
        }, 60000); // 每分钟清理一次
    }
    
    cleanupExpiredCache() {
        const now = Date.now();
        for (const [key, entry] of this.cache.entries()) {
            if (now - entry.timestamp > entry.ttl) {
                this.cache.delete(key);
            }
        }
    }
    
    cleanupUnusedResources() {
        const oneHourAgo = Date.now() - 60 * 60 * 1000;
        
        for (const [key, entry] of this.cache.entries()) {
            if (entry.lastAccess && entry.lastAccess < oneHourAgo) {
                this.cache.delete(key);
            }
        }
    }
    
    // 内存警告处理
    setupMemoryWarning() {
        if (performance.memory) {
            setInterval(() => {
                const usedMB = performance.memory.usedJSHeapSize / 1024 / 1024;
                const totalMB = performance.memory.jsHeapSizeLimit / 1024 / 1024;
                
                if (usedMB / totalMB > 0.8) { // 使用超过80%
                    this.forceCleanup();
                }
            }, 10000);
        }
    }
    
    forceCleanup() {
        // 清理所有缓存
        this.cache.clear();
        
        // 触发垃圾回收
        if (window.gc) {
            window.gc();
        }
        
        console.warn('强制内存清理已执行');
    }
    
    estimateSize(obj) {
        const str = JSON.stringify(obj);
        return new Blob([str]).size;
    }
    
    getTotalCacheSize() {
        let total = 0;
        for (const entry of this.cache.values()) {
            total += entry.size;
        }
        return total;
    }
}

6. 总结与展望

本项目创建了一个基于Impress.js的3D立方体旋转个人年终总结展示系统,具有以下特点:

  1. 创新的3D导航:通过立方体旋转在不同主题间切换

  2. 艺术性设计:精心设计的视觉层次和动画效果

  3. 数据可视化:集成多种图表展示年度数据

  4. 完整的技术栈:HTML5、CSS3、JavaScript原生实现

  5. 性能优化:多重优化策略确保流畅体验

  6. 可访问性:全面支持屏幕阅读器和键盘导航

  7. 响应式设计:适配各种设备和屏幕尺寸

相关推荐
jllllyuz2 小时前
室外可见光通信信道建模与MATLAB实现(直射链路与反射链路)
开发语言·matlab
榴莲不好吃2 小时前
前端js图片压缩
开发语言·前端·javascript
切糕师学AI2 小时前
Vue 中的 keep-alive 组件
前端·javascript·vue.js
散峰而望2 小时前
【数据结构】假如数据排排坐:顺序表的秩序世界
java·c语言·开发语言·数据结构·c++·算法·github
superman超哥2 小时前
自定义序列化逻辑:掌控数据编码的每一个细节
开发语言·rust·编程语言·rust自定义序列化·rust数据编码
可问春风_ren2 小时前
Git命令大全
前端·javascript·git·后端
她说彩礼65万2 小时前
Jquery总结
前端·javascript·jquery
jiayong232 小时前
JVM垃圾回收机制面试题
java·开发语言·jvm
得一录2 小时前
ES6核心语法
前端·ecmascript·es6