分享图
动画工坊
引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>微观机械原理3D极慢速自锁透视动画</title>
    <style>
        :root {
            --bg-color: #050914;
            --grid-color: rgba(30, 58, 138, 0.2);
            --shell-normal: #334155;
            --shell-xray: rgba(0, 229, 255, 0.1);
            --shell-stroke-xray: #00e5ff;
            --steel-color: #64748b;
            --mech-primary: #f59e0b;
            --mech-secondary: #ef4444;
            --hud-text: #38bdf8;
            --force-glow: #39ff14;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100vw;
            height: 100vh;
            background-color: var(--bg-color);
            background-image: 
                linear-gradient(var(--grid-color) 1px, transparent 1px),
                linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
            background-size: 40px 40px;
            background-position: center center;
            overflow: hidden;
            font-family: 'Courier New', Courier, monospace;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        /* 扫描线特效 */
        .scanline {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: linear-gradient(to bottom, rgba(255,255,255,0), rgba(255,255,255,0) 50%, rgba(0, 229, 255, 0.05) 50%, rgba(0, 229, 255, 0.05));
            background-size: 100% 4px;
            pointer-events: none;
            z-index: 10;
        }

        /* HUD - 绝对边缘与角落布局,字体极小,绝不遮挡中心 */
        .hud-panel {
            position: absolute;
            color: var(--hud-text);
            font-size: 12px;
            letter-spacing: 1px;
            z-index: 20;
            opacity: 0.8;
            pointer-events: none;
            text-transform: uppercase;
        }
        
        .hud-top-left { top: 20px; left: 20px; }
        .hud-top-right { top: 20px; right: 20px; text-align: right; }
        .hud-bottom-left { bottom: 20px; left: 20px; }
        .hud-bottom-right { bottom: 20px; right: 20px; text-align: right; }

        .hud-value {
            color: #fff;
            font-weight: bold;
            text-shadow: 0 0 5px var(--hud-text);
        }

        .hud-alert {
            color: var(--mech-secondary);
            animation: pulse-alert 1s infinite;
            display: none;
        }

        @keyframes pulse-alert {
            0%, 100% { opacity: 0.3; }
            50% { opacity: 1; text-shadow: 0 0 8px var(--mech-secondary); }
        }

        /* 核心动画容器 */
        #animation-viewport {
            width: 100vw;
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            /* 初始宏观视角 */
            transform: scale(0.35) translateY(100px);
            transition: transform 0.8s cubic-bezier(0.8, 0, 0.2, 1);
            will-change: transform;
        }

        /* 震动特效 */
        @keyframes camera-shake {
            0% { transform: scale(1.6) translate(0, 0); }
            20% { transform: scale(1.6) translate(-5px, 3px); }
            40% { transform: scale(1.6) translate(4px, -4px); }
            60% { transform: scale(1.6) translate(-3px, 5px); }
            80% { transform: scale(1.6) translate(5px, -2px); }
            100% { transform: scale(1.6) translate(0, 0); }
        }

        /* SVG 样式 */
        svg {
            width: 1000px;
            height: 1000px;
            overflow: visible;
        }

        /* 部件默认与过渡样式 */
        .tool-assembly {
            transform: translateY(800px);
            transition: transform 0.3s cubic-bezier(0.1, 1, 0.1, 1);
        }

        .shell {
            fill: var(--shell-normal);
            stroke: #1e293b;
            stroke-width: 2;
            transition: all 1s ease-in-out;
        }

        .internal-mech {
            opacity: 0;
            transition: opacity 1s ease-in-out;
        }

        .eccentric-wheel {
            transform-origin: 580px 480px; /* 偏心轴点 */
            transform: rotate(0deg);
            fill: var(--mech-primary);
            stroke: #fff;
            stroke-width: 2;
        }

        .locking-tongue {
            transform-origin: 580px 600px;
            transform: rotate(-15deg);
            fill: var(--mech-primary);
        }

        .spring {
            transform-origin: center bottom;
            stroke: #cbd5e1;
            stroke-width: 6;
            fill: none;
            stroke-linecap: round;
            stroke-linejoin: round;
        }

        .force-arrow {
            opacity: 0;
            fill: var(--force-glow);
            filter: drop-shadow(0 0 8px var(--force-glow));
        }
        
        .spark {
            opacity: 0;
            fill: #fff;
        }

        /*=========================================
          动画阶段状态类 (由 JS 切换)
        =========================================*/
        
        /* 1. 接近瞬间 */
        .phase-approach .tool-assembly {
            transform: translateY(0);
        }

        /* 2. 镜头拉近与撞击震动 */
        .phase-zoom #animation-viewport {
            transform: scale(1.6) translateY(20px);
            animation: camera-shake 0.3s cubic-bezier(.36,.07,.19,.97) both;
        }

        /* 3. X射线透视模式开启 */
        .phase-xray .shell {
            fill: var(--shell-xray);
            stroke: var(--shell-stroke-xray);
            stroke-width: 3;
            filter: drop-shadow(0 0 15px rgba(0, 229, 255, 0.4));
        }
        .phase-xray .internal-mech {
            opacity: 1;
        }

        /* 4. 超慢动作咬合 (120fps 慢放视觉) */
        .phase-slowmo .eccentric-wheel {
            transform: rotate(-55deg);
            transition: transform 3.5s cubic-bezier(0.2, 0.8, 0.2, 1);
        }
        .phase-slowmo .locking-tongue {
            transform: rotate(0deg);
            transition: transform 3.5s cubic-bezier(0.2, 0.8, 0.2, 1);
        }
        .phase-slowmo .spring-compress {
            /* 模拟弹簧压缩 */
            transform: translateY(5px) scaleY(0.7);
            transition: transform 3.5s cubic-bezier(0.2, 0.8, 0.2, 1);
        }

        /* 5. 锁定与力学矢量展示 */
        .phase-force .force-arrow {
            opacity: 1;
            transition: opacity 0.5s ease, transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
            /* 箭头从中心往外推的动画 */
        }
        .phase-force .arrow-1 { transform: translate(-10px, 10px); }
        .phase-force .arrow-2 { transform: translate(15px, 0); }
        
        .phase-force .spark {
            animation: spark-burst 0.5s ease-out forwards;
        }

        @keyframes spark-burst {
            0% { opacity: 1; transform: scale(0.5) translate(0,0); }
            100% { opacity: 0; transform: scale(2) translate(var(--tx), var(--ty)); }
        }

        .phase-force .hud-alert {
            display: block;
        }

    </style>
</head>
<body>
    <div class="scanline"></div>

    <!-- 极简HUD,置于绝对角落 -->
    <div class="hud-panel hud-top-left">
        <div>System: <span class="hud-value">AUTO-LOCKING RIG</span></div>
        <div>Cam Mode: <span class="hud-value" id="hud-cam">MACRO_WIDE</span></div>
        <div style="margin-top: 10px; color: #00e5ff;">[IFR_ACHIEVED: DIRECT_ENGAGEMENT]</div>
    </div>
    
    <div class="hud-panel hud-top-right">
        <div>Capture Rate: <span class="hud-value" id="hud-fps">24 FPS</span></div>
        <div>Material Alpha: <span class="hud-value" id="hud-alpha">1.00</span></div>
    </div>

    <div class="hud-panel hud-bottom-left">
        <div class="hud-alert" id="hud-alert">>>> LOCKING SEQUENCE INITIATED <<<</div>
        <div style="margin-top: 5px;">TIME DILATION: <span class="hud-value" id="hud-time">1.0x</span></div>
    </div>

    <div class="hud-panel hud-bottom-right">
        <div>Tension Force: <span class="hud-value" id="hud-force">0.0 kN</span></div>
        <div>Status: <span class="hud-value" id="hud-status" style="color: #94a3b8;">APPROACHING</span></div>
    </div>

    <!-- 核心视觉容器 -->
    <div id="animation-viewport">
        <svg viewBox="0 0 1000 1000" xmlns="http://www.w3.org/2000/svg">
            <defs>
                <pattern id="steel-texture" width="20" height="20" patternUnits="userSpaceOnUse">
                    <line x1="0" y1="0" x2="20" y2="20" stroke="#475569" stroke-width="1" opacity="0.3"/>
                </pattern>
                
                <filter id="glow-neon">
                    <feGaussianBlur stdDeviation="3" result="coloredBlur"/>
                    <feMerge>
                        <feMergeNode in="coloredBlur"/>
                        <feMergeNode in="SourceGraphic"/>
                    </feMerge>
                </filter>
            </defs>

            <!-- 环境基准:角钢横担 (Target) -->
            <g id="angle-steel" transform="translate(420, 250)">
                <path d="M 0 0 h 60 v 250 h -60 z" fill="var(--steel-color)"/>
                <path d="M 0 0 h 200 v 40 h -200 z" fill="var(--steel-color)"/>
                <path d="M 0 0 h 60 v 250 h -60 z" fill="url(#steel-texture)"/>
                <path d="M 0 0 h 200 v 40 h -200 z" fill="url(#steel-texture)"/>
                <line x1="60" y1="40" x2="60" y2="250" stroke="#334155" stroke-width="4"/>
                <line x1="60" y1="40" x2="200" y2="40" stroke="#334155" stroke-width="4"/>
            </g>

            <!-- 动态升降工具组 -->
            <g class="tool-assembly">
                <!-- 内部机械结构 (X射线透视后可见) -->
                <g class="internal-mech">
                    <!-- 弹簧与底座 -->
                    <g transform="translate(580, 660)">
                        <rect x="-15" y="0" width="30" height="20" fill="#64748b" />
                        <path class="spring spring-compress" d="M 0 0 L 10 -20 L -10 -40 L 10 -60 L -10 -80 L 0 -100" />
                    </g>

                    <!-- 单向锁舌 (棘爪) -->
                    <path class="locking-tongue" d="M 570 610 L 610 590 L 600 550 L 560 550 Z" />
                    <circle cx="580" cy="600" r="8" fill="#1e293b"/>

                    <!-- 偏心轮与齿槽 (棘轮) -->
                    <g class="eccentric-wheel">
                        <!-- 偏心轮主体外廓 (咬合面) -->
                        <path d="M 580 480 C 450 480, 430 350, 480 320 C 530 290, 650 350, 660 450 C 660 510, 620 520, 580 480 Z" />
                        <!-- 棘轮齿 -->
                        <path d="M 640 450 L 660 440 L 650 420 L 670 410 L 660 390 L 680 380 L 660 360 L 670 350 L 640 330 Z" fill="var(--mech-primary)" stroke-linejoin="round"/>
                        <!-- 轴心与偏心距指示 -->
                        <circle cx="580" cy="480" r="12" fill="#0f172a" stroke="#fff" stroke-width="3"/>
                        <circle cx="520" cy="410" r="6" fill="#0f172a" stroke="#fff" stroke-width="2"/> <!-- 几何中心 -->
                        <line x1="580" y1="480" x2="520" y2="410" stroke="#00e5ff" stroke-width="2" stroke-dasharray="4,2"/>
                    </g>
                </g>

                <!-- 外壳 (透明度变化) -->
                <path class="shell" d="M 400 700 L 400 350 L 460 350 L 460 600 C 460 620, 480 650, 520 650 L 680 650 L 680 300 L 720 300 L 720 700 Z"/>
                
                <!-- 固定夹爪内垫片 -->
                <rect x="440" y="360" width="20" height="80" fill="#94a3b8" />

                <!-- 力学分析效果层 (透明) -->
                <g class="force-overlays">
                    <!-- 左侧反作用力箭头 -->
                    <g class="force-arrow arrow-1" transform="translate(460, 400)">
                        <path d="M 0 0 L -30 -15 L -25 -5 L -50 -5 L -50 5 L -25 5 L -30 15 Z" />
                        <text x="-65" y="4" fill="var(--force-glow)" font-size="14" font-weight="bold" filter="url(#glow-neon)">Fn</text>
                    </g>
                    <!-- 右侧偏心压紧力箭头 -->
                    <g class="force-arrow arrow-2" transform="translate(480, 400)">
                        <path d="M 0 0 L 30 -15 L 25 -5 L 50 -5 L 50 5 L 25 5 L 30 15 Z" />
                        <text x="55" y="4" fill="var(--force-glow)" font-size="14" font-weight="bold" filter="url(#glow-neon)">Fc</text>
                    </g>
                    
                    <!-- 咬合瞬间火花特效 -->
                    <g transform="translate(470, 400)">
                        <circle class="spark" cx="0" cy="0" r="3" style="--tx: -20px; --ty: -30px;"/>
                        <circle class="spark" cx="0" cy="0" r="2" style="--tx: -30px; --ty: 10px;"/>
                        <circle class="spark" cx="0" cy="0" r="4" style="--tx: 10px; --ty: -40px;"/>
                        <circle class="spark" cx="0" cy="0" r="2" style="--tx: 25px; --ty: -20px;"/>
                    </g>
                </g>
            </g>
        </svg>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const body = document.body;
            const uiCam = document.getElementById('hud-cam');
            const uiFps = document.getElementById('hud-fps');
            const uiAlpha = document.getElementById('hud-alpha');
            const uiTime = document.getElementById('hud-time');
            const uiForce = document.getElementById('hud-force');
            const uiStatus = document.getElementById('hud-status');

            // 动画状态机流水线
            const timeline = [
                { time: 500, action: () => { 
                    body.classList.add('phase-approach'); 
                    uiStatus.innerText = 'IMPACT_IMMINENT';
                    uiStatus.style.color = '#f59e0b';
                }},
                { time: 900, action: () => { 
                    body.classList.add('phase-zoom'); 
                    uiCam.innerText = 'MICRO_TRACKING';
                    uiCam.style.color = '#00e5ff';
                    uiStatus.innerText = 'CONTACT';
                }},
                { time: 1400, action: () => { 
                    body.classList.add('phase-xray'); 
                    uiAlpha.innerText = '0.30';
                    uiAlpha.style.color = '#00e5ff';
                }},
                { time: 2000, action: () => { 
                    body.classList.add('phase-slowmo');
                    uiFps.innerText = '120 FPS';
                    uiFps.style.color = '#00e5ff';
                    uiTime.innerText = '0.01x (SUPER SLOW)';
                    uiTime.style.color = '#f59e0b';
                    uiStatus.innerText = 'SELF_LOCKING...';
                }},
                { time: 5500, action: () => { 
                    body.classList.add('phase-force');
                    uiStatus.innerText = 'SECURED';
                    uiStatus.style.color = '#39ff14';
                    animateValue(uiForce, 0, 58.4, 800, ' kN');
                }},
                { time: 9000, action: () => { 
                    // 重置,形成无限循环
                    body.className = '';
                    uiCam.innerText = 'MACRO_WIDE';
                    uiCam.style.color = '#fff';
                    uiFps.innerText = '24 FPS';
                    uiFps.style.color = '#fff';
                    uiAlpha.innerText = '1.00';
                    uiAlpha.style.color = '#fff';
                    uiTime.innerText = '1.0x';
                    uiTime.style.color = '#fff';
                    uiStatus.innerText = 'APPROACHING';
                    uiStatus.style.color = '#94a3b8';
                    uiForce.innerText = '0.0 kN';
                }}
            ];

            // 数值滚动动画函数
            function animateValue(obj, start, end, duration, suffix) {
                let startTimestamp = null;
                const step = (timestamp) => {
                    if (!startTimestamp) startTimestamp = timestamp;
                    const progress = Math.min((timestamp - startTimestamp) / duration, 1);
                    obj.innerHTML = (progress * (end - start) + start).toFixed(1) + suffix;
                    if (progress < 1) {
                        window.requestAnimationFrame(step);
                    }
                };
                window.requestAnimationFrame(step);
            }

            // 循环控制
            function runSequence() {
                timeline.forEach(step => {
                    setTimeout(step.action, step.time);
                });
            }

            // 加载完成后直接启动并循环
            runSequence();
            setInterval(runSequence, 10000); // 每10秒完整循环一次
        });
    </script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>弹簧效应 - 阻尼谐振子动态物理模型</title>
    <style>
        :root {
            --bg-base: #030712;
            --grid-color: rgba(14, 165, 233, 0.1);
            --hud-color: #38bdf8;
            --hud-alert: #f43f5e;
            --hud-text-size: 11px;
            --spring-rest: #0ea5e9;
            --spring-max: #ef4444;
            --metal-dark: #1e293b;
            --metal-light: #475569;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100vw;
            height: 100vh;
            background-color: var(--bg-base);
            background-image: 
                radial-gradient(circle at center, rgba(14, 165, 233, 0.05) 0%, transparent 70%),
                linear-gradient(var(--grid-color) 1px, transparent 1px),
                linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
            background-size: 100% 100%, 50px 50px, 50px 50px;
            background-position: center center;
            overflow: hidden;
            font-family: 'SF Pro Display', 'Roboto Mono', monospace;
            display: flex;
            justify-content: center;
            align-items: center;
            color: #fff;
        }

        /* 扫描线与暗角 */
        .vignette {
            position: absolute;
            inset: 0;
            background: radial-gradient(circle at center, transparent 40%, rgba(0,0,0,0.8) 100%);
            pointer-events: none;
            z-index: 5;
        }

        .scanline {
            position: absolute;
            inset: 0;
            background: linear-gradient(to bottom, transparent 50%, rgba(14, 165, 233, 0.03) 51%);
            background-size: 100% 4px;
            pointer-events: none;
            z-index: 6;
        }

        /* HUD 系统 - 极端边角布局,超小字体,严禁遮挡中心 */
        .hud-container {
            position: absolute;
            z-index: 20;
            font-size: var(--hud-text-size);
            letter-spacing: 1.5px;
            text-transform: uppercase;
            opacity: 0.85;
            pointer-events: none;
            text-shadow: 0 0 4px rgba(56, 189, 248, 0.5);
        }

        .hud-tl { top: 20px; left: 24px; }
        .hud-tr { top: 20px; right: 24px; text-align: right; }
        .hud-bl { bottom: 20px; left: 24px; }
        .hud-br { bottom: 20px; right: 24px; text-align: right; }

        .hud-title {
            color: var(--hud-color);
            font-weight: 700;
            margin-bottom: 6px;
            font-size: 12px;
            border-bottom: 1px solid rgba(56, 189, 248, 0.3);
            padding-bottom: 4px;
        }

        .hud-row {
            margin-bottom: 4px;
            display: flex;
            justify-content: space-between;
            gap: 16px;
        }

        .hud-val {
            color: #fff;
            font-family: monospace;
            font-weight: bold;
        }

        /* 能量条 UI */
        .energy-bar-bg {
            width: 120px;
            height: 4px;
            background: rgba(255,255,255,0.1);
            margin-top: 4px;
            position: relative;
            overflow: hidden;
        }

        .energy-bar-fill {
            height: 100%;
            width: 0%;
            transition: width 0.1s linear, background-color 0.1s;
        }

        #bar-pe { background: var(--spring-max); box-shadow: 0 0 8px var(--spring-max); }
        #bar-ke { background: var(--spring-rest); box-shadow: 0 0 8px var(--spring-rest); }

        /* 核心动画视口 */
        #viewport {
            width: 100%;
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        svg {
            width: 100%;
            height: 100%;
            max-width: 1200px;
            max-height: 1200px;
            overflow: visible;
        }

        /* SVG 元素样式 */
        .mech-base {
            fill: var(--metal-dark);
            stroke: var(--metal-light);
            stroke-width: 2;
        }

        .spring-wire {
            fill: none;
            stroke-width: 12;
            stroke-linecap: round;
            stroke-linejoin: round;
            /* 发光效果通过内联样式结合 JS 动态修改 */
            transition: stroke 0.1s ease-out;
            filter: drop-shadow(0 0 10px currentColor);
        }

        .weight-block {
            fill: #0f172a;
            stroke: #cbd5e1;
            stroke-width: 3;
        }

        .weight-core {
            fill: rgba(14, 165, 233, 0.2);
            stroke: var(--hud-color);
            stroke-width: 1;
        }

        .force-vector {
            fill: none;
            stroke: var(--hud-alert);
            stroke-width: 3;
            marker-end: url(#arrow-head);
            transition: opacity 0.2s;
            opacity: 0;
        }

        .grid-line {
            stroke: rgba(255,255,255,0.1);
            stroke-width: 1;
            stroke-dasharray: 4 4;
        }
    </style>
</head>
<body>
    <div class="vignette"></div>
    <div class="scanline"></div>

    <!-- HUD Top Left -->
    <div class="hud-container hud-tl">
        <div class="hud-title">弹簧效应 // 阻尼谐振子动态模型</div>
        <div class="hud-row"><span>SYS_STATUS:</span> <span class="hud-val" id="sys-status" style="color: var(--hud-color);">INITIALIZING</span></div>
        <div class="hud-row"><span>PHASE:</span> <span class="hud-val" id="sys-phase">IDLE</span></div>
    </div>

    <!-- HUD Top Right -->
    <div class="hud-container hud-tr">
        <div class="hud-title">物理定律方程组 // KINEMATICS</div>
        <div class="hud-row"><span>HOOKE'S LAW:</span> <span class="hud-val">F = -k·Δx</span></div>
        <div class="hud-row"><span>DAMPING:</span> <span class="hud-val">Fd = -c·v</span></div>
        <div class="hud-row"><span>CONSTANT (k):</span> <span class="hud-val">0.08 N/m</span></div>
        <div class="hud-row"><span>DAMPING (c):</span> <span class="hud-val">0.975</span></div>
    </div>

    <!-- HUD Bottom Left -->
    <div class="hud-container hud-bl">
        <div class="hud-title">系统能量转化 // THERMODYNAMICS</div>
        <div class="hud-row" style="display: block;">
            <div style="display: flex; justify-content: space-between;">
                <span>POTENTIAL ENG (Ep):</span> <span class="hud-val" id="val-pe">0.00 J</span>
            </div>
            <div class="energy-bar-bg"><div class="energy-bar-fill" id="bar-pe"></div></div>
        </div>
        <div class="hud-row" style="display: block; margin-top: 10px;">
            <div style="display: flex; justify-content: space-between;">
                <span>KINETIC ENG (Ek):</span> <span class="hud-val" id="val-ke">0.00 J</span>
            </div>
            <div class="energy-bar-bg"><div class="energy-bar-fill" id="bar-ke"></div></div>
        </div>
    </div>

    <!-- HUD Bottom Right -->
    <div class="hud-container hud-br">
        <div class="hud-title">实时遥测数据 // TELEMETRY</div>
        <div class="hud-row"><span>DISPLACEMENT (Δx):</span> <span class="hud-val" id="val-disp">+0.00 mm</span></div>
        <div class="hud-row"><span>VELOCITY (v):</span> <span class="hud-val" id="val-vel">0.00 m/s</span></div>
        <div class="hud-row"><span>RESTORING FORCE (F):</span> <span class="hud-val" id="val-force">0.00 N</span></div>
    </div>

    <!-- 核心 SVG 动画区 -->
    <div id="viewport">
        <svg viewBox="0 0 1000 1000" preserveAspectRatio="xMidYMid meet">
            <defs>
                <marker id="arrow-head" markerWidth="10" markerHeight="10" refX="5" refY="5" orient="auto-start-reverse">
                    <path d="M 0 0 L 10 5 L 0 10 z" fill="var(--hud-alert)" />
                </marker>
                <pattern id="carbon" width="10" height="10" patternUnits="userSpaceOnUse">
                    <path d="M0 10L10 0ZM-1 1L1 -1ZM9 11L11 9Z" stroke="rgba(255,255,255,0.1)" stroke-width="1"/>
                </pattern>
                <filter id="glow-intense">
                    <feGaussianBlur stdDeviation="5" result="coloredBlur"/>
                    <feMerge>
                        <feMergeNode in="coloredBlur"/>
                        <feMergeNode in="SourceGraphic"/>
                    </feMerge>
                </filter>
            </defs>

            <!-- 背景刻度标尺 -->
            <g transform="translate(650, 0)">
                <line x1="0" y1="200" x2="0" y2="900" class="grid-line" />
                <line x1="-10" y1="500" x2="20" y2="500" stroke="#0ea5e9" stroke-width="2" />
                <text x="30" y="504" fill="#0ea5e9" font-size="12" font-family="monospace">ORIGIN (0)</text>
                
                <line x1="0" y1="300" x2="10" y2="300" class="grid-line" />
                <text x="20" y="304" fill="#64748b" font-size="10">-200</text>
                
                <line x1="0" y1="700" x2="10" y2="700" class="grid-line" />
                <text x="20" y="704" fill="#64748b" font-size="10">+200</text>
                
                <line x1="0" y1="900" x2="10" y2="900" class="grid-line" />
                <text x="20" y="904" fill="#64748b" font-size="10">+400</text>
            </g>

            <!-- 顶部固定基座 -->
            <g transform="translate(500, 100)">
                <rect x="-80" y="0" width="160" height="40" rx="5" class="mech-base" fill="url(#carbon)" />
                <path d="M -40 40 L -20 80 L 20 80 L 40 40 Z" class="mech-base" />
                <circle cx="0" cy="70" r="8" fill="#030712" stroke="#475569" stroke-width="2" />
            </g>

            <!-- 动态弹簧路径 -->
            <path id="spring-wire" class="spring-wire" d="" stroke="#0ea5e9" />

            <!-- 动态配重块及受力箭头 -->
            <g id="weight-group" transform="translate(500, 500)">
                <!-- 恢复力矢量箭头 -->
                <line id="force-arrow" x1="90" y1="0" x2="90" y2="0" class="force-vector" />
                
                <!-- 速度矢量箭头 (向左偏一点避免重叠) -->
                <line id="vel-arrow" x1="-90" y1="0" x2="-90" y2="0" class="force-vector" stroke="#0ea5e9" />

                <!-- 挂钩处 -->
                <path d="M 0 -20 L 0 -50" stroke="#94a3b8" stroke-width="6" fill="none" />
                
                <!-- 配重主体 -->
                <rect x="-60" y="-20" width="120" height="80" rx="10" class="weight-block" fill="url(#carbon)"/>
                
                <!-- 发光核心/传感器显示 -->
                <rect x="-40" y="0" width="80" height="40" rx="4" class="weight-core" />
                <circle cx="-20" cy="20" r="4" fill="#38bdf8" id="sensor-led" filter="url(#glow-intense)"/>
                <line x1="0" y1="10" x2="0" y2="30" stroke="rgba(255,255,255,0.2)" stroke-width="2" />
                <line x1="10" y1="12" x2="10" y2="28" stroke="rgba(255,255,255,0.2)" stroke-width="2" />
                <line x1="20" y1="15" x2="20" y2="25" stroke="rgba(255,255,255,0.2)" stroke-width="2" />
                
                <!-- 当前刻度指示器 -->
                <path d="M 60 20 L 75 20 L 80 15 L 80 25 Z" fill="#0ea5e9" />
                <line x1="75" y1="20" x2="140" y2="20" stroke="#0ea5e9" stroke-width="1" stroke-dasharray="2 2" />
            </g>
        </svg>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', () => {
            // DOM 元素缓存
            const springEl = document.getElementById('spring-wire');
            const weightEl = document.getElementById('weight-group');
            const forceArrowEl = document.getElementById('force-arrow');
            const velArrowEl = document.getElementById('vel-arrow');
            const sensorLed = document.getElementById('sensor-led');
            
            // HUD 元素
            const statusEl = document.getElementById('sys-status');
            const phaseEl = document.getElementById('sys-phase');
            const dispEl = document.getElementById('val-disp');
            const velEl = document.getElementById('val-vel');
            const forceEl = document.getElementById('val-force');
            const peEl = document.getElementById('val-pe');
            const keEl = document.getElementById('val-ke');
            const barPe = document.getElementById('bar-pe');
            const barKe = document.getElementById('bar-ke');

            // 物理常量与变量
            const yTop = 180;        // 弹簧顶部固定Y坐标
            const restY = 500;       // 平衡位置 (物块中心Y坐标)
            const k = 0.08;          // 弹簧劲度系数
            const damping = 0.985;   // 阻尼系数 (1.0为无阻尼)
            const mass = 1.0;        // 质量
            
            let currentY = restY;
            let velocityY = 0;
            
            // 状态机: 0: 等待, 1: 下拉蓄能, 2: 释放谐振
            let state = 0; 
            let pullTargetY = 880;   // 最大拉伸位置
            let pullProgress = 0;

            // 颜色插值函数 (基于形变程度,将颜色从冷蓝变为炽红)
            function getStressColor(displacement, maxDisp = 400) {
                const ratio = Math.min(Math.abs(displacement) / maxDisp, 1);
                // 蓝(14, 165, 233) -> 红(239, 68, 68)
                const r = Math.round(14 + ratio * (239 - 14));
                const g = Math.round(165 + ratio * (68 - 165));
                const b = Math.round(233 + ratio * (68 - 233));
                return `rgb(${r}, ${g}, ${b})`;
            }

            // 生成3D质感折线弹簧路径
            function generateSpringPath(yBottom) {
                const coils = 16;
                const width = 45;
                const centerX = 500;
                // 从顶部连接点开始
                let path = `M ${centerX} ${yTop} L ${centerX} ${yTop + 20} `;
                
                const springHeight = (yBottom - 40) - (yTop + 20);
                const step = springHeight / coils;
                
                for (let i = 0; i <= coils; i++) {
                    const py = (yTop + 20) + (i * step);
                    // 交替左右形成螺旋视觉
                    const px = i % 2 === 0 ? centerX + width : centerX - width;
                    // 使用二次贝塞尔曲线让拐角圆滑,增加工业机械感
                    if (i === 0) {
                        path += `L ${px} ${py} `;
                    } else {
                        // 计算控制点以平滑过渡
                        path += `L ${px} ${py} `;
                    }
                }
                
                // 连接到底部挂钩
                path += `L ${centerX} ${yBottom - 20} L ${centerX} ${yBottom}`;
                return path;
            }

            // 主渲染与物理循环
            function render() {
                // 物理状态更新
                if (state === 1) {
                    // 下拉蓄能阶段 (强制匀速拉伸)
                    pullProgress += 0.005;
                    // 使用 easeInOut 曲线让下拉显得有阻力且沉重
                    const ease = pullProgress < 0.5 ? 2 * pullProgress * pullProgress : -1 + (4 - 2 * pullProgress) * pullProgress;
                    currentY = restY + (pullTargetY - restY) * ease;
                    
                    if (pullProgress >= 1) {
                        state = 2; // 达到最大拉力,释放
                        velocityY = 0;
                        phaseEl.innerText = "RELEASE -> HARMONIC OSCILLATION";
                        phaseEl.style.color = "#39ff14"; // 绿色
                        statusEl.innerText = "ACTIVE_SIMULATION";
                        statusEl.style.color = "#39ff14";
                        forceArrowEl.style.opacity = 1;
                        velArrowEl.style.opacity = 1;
                    }
                } else if (state === 2) {
                    // 释放后的阻尼谐振物理引擎 (胡克定律 + 阻尼)
                    const displacement = currentY - restY;
                    const restoringForce = -k * displacement;
                    
                    // a = F / m
                    const acceleration = restoringForce / mass;
                    
                    // v = v + a
                    velocityY += acceleration;
                    
                    // v = v * damping
                    velocityY *= damping;
                    
                    // y = y + v
                    currentY += velocityY;

                    // 判断能量衰减殆尽,重置循环
                    if (Math.abs(displacement) < 1 && Math.abs(velocityY) < 0.1) {
                        state = 0;
                        setTimeout(() => {
                            state = 1;
                            pullProgress = 0;
                            phaseEl.innerText = "POTENTIAL ENERGY BUILDUP";
                            phaseEl.style.color = "#ef4444"; // 红色
                            statusEl.innerText = "EXTERNAL_FORCE_APPLIED";
                            statusEl.style.color = "#ef4444";
                            forceArrowEl.style.opacity = 0;
                            velArrowEl.style.opacity = 0;
                        }, 2000); // 停顿2秒后重新开始
                    }
                }

                // 核心变量计算
                const displacement = currentY - restY;
                const force = -k * displacement;
                const pe = 0.5 * k * displacement * displacement; // 势能 Ep = 1/2 k x^2
                const ke = 0.5 * mass * velocityY * velocityY;    // 动能 Ek = 1/2 m v^2
                
                // 视觉更新
                // 1. 更新配重位置
                weightEl.setAttribute('transform', `translate(500, ${currentY})`);
                
                // 2. 更新弹簧路径与颜色
                springEl.setAttribute('d', generateSpringPath(currentY - 20)); // -20是挂钩偏移
                const stressColor = getStressColor(displacement);
                springEl.style.stroke = stressColor;
                sensorLed.style.fill = stressColor;
                
                // 3. 动态更新力与速度矢量箭头 (仅在震荡阶段显示显著)
                if (state === 2) {
                    // 力的方向与位移相反
                    forceArrowEl.setAttribute('y2', force * 3); // 视觉放大系数
                    // 速度方向
                    velArrowEl.setAttribute('y2', velocityY * 5); // 视觉放大系数
                }

                // 4. 更新 HUD 数据 (节流减少DOM操作开销,每帧更新纯数值)
                const sign = displacement >= 0 ? '+' : '';
                dispEl.innerText = `${sign}${displacement.toFixed(2)} mm`;
                dispEl.style.color = stressColor;

                const velSign = velocityY >= 0 ? '+' : '';
                velEl.innerText = `${velSign}${velocityY.toFixed(2)} m/s`;

                const forceSign = force >= 0 ? '+' : '';
                forceEl.innerText = `${forceSign}${force.toFixed(2)} N`;

                peEl.innerText = `${pe.toFixed(2)} J`;
                keEl.innerText = `${ke.toFixed(2)} J`;

                // 能量条宽度更新 (假定最大能量为 6500)
                const maxEng = 6500;
                barPe.style.width = `${Math.min((pe / maxEng) * 100, 100)}%`;
                barPe.style.backgroundColor = stressColor;
                barPe.style.boxShadow = `0 0 8px ${stressColor}`;
                
                barKe.style.width = `${Math.min((ke / maxEng) * 100, 100)}%`;

                // 循环渲染
                requestAnimationFrame(render);
            }

            // 启动序列
            setTimeout(() => {
                state = 1; // 开始下拉
                phaseEl.innerText = "POTENTIAL ENERGY BUILDUP";
                phaseEl.style.color = "#ef4444";
                statusEl.innerText = "EXTERNAL_FORCE_APPLIED";
                statusEl.style.color = "#ef4444";
            }, 1000);

            // 启动物理引擎
            render();
        });
    </script>
</body>
</html>
积分规则:第一轮对话扣减8分,后续每轮扣6分