分享图
动画工坊
引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TRIZ IFR - 双金属热驱动自锁机构</title>
    <style>
        :root {
            --bg-color: #0b1120;
            --grid-color: rgba(255, 255, 255, 0.05);
            --text-main: #f8fafc;
            --text-muted: #94a3b8;
            --accent-hot: #ff4713;
            --accent-cold: #38bdf8;
            --panel-bg: rgba(15, 23, 42, 0.7);
            --panel-border: rgba(51, 65, 85, 0.6);
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            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: 30px 30px;
            font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
            color: var(--text-main);
            display: flex;
            justify-content: center;
            align-items: center;
            overflow: hidden;
        }

        .container {
            position: relative;
            width: 100%;
            max-width: 1200px;
            height: 100vh;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
        }

        svg {
            width: 100%;
            height: 100%;
            max-height: 85vh;
            filter: drop-shadow(0 0 30px rgba(0,0,0,0.5));
        }

        /* 绝对定位的文本信息面板层,避免遮挡中心动画区域 */
        .info-panel {
            position: absolute;
            background: var(--panel-bg);
            border: 1px solid var(--panel-border);
            padding: 16px 20px;
            border-radius: 8px;
            backdrop-filter: blur(8px);
            font-size: 13px;
            line-height: 1.6;
            z-index: 10;
        }

        .panel-top-left { top: 30px; left: 40px; }
        .panel-top-right { top: 30px; right: 40px; }
        .panel-bottom-left { bottom: 30px; left: 40px; }
        .panel-bottom-right { bottom: 30px; right: 40px; }

        .title {
            font-size: 18px;
            font-weight: bold;
            color: var(--text-main);
            margin-bottom: 8px;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .title::before {
            content: '';
            display: block;
            width: 4px;
            height: 16px;
            background: var(--accent-hot);
            border-radius: 2px;
        }

        .badge {
            display: inline-block;
            padding: 2px 8px;
            border-radius: 12px;
            font-size: 11px;
            font-weight: bold;
            background: rgba(255, 71, 19, 0.2);
            color: var(--accent-hot);
            border: 1px solid rgba(255, 71, 19, 0.4);
            margin-bottom: 12px;
        }

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

        .data-label { color: var(--text-muted); }
        
        .status-indicator {
            display: flex;
            align-items: center;
            gap: 6px;
            font-size: 15px;
            font-weight: bold;
            margin-top: 10px;
            padding-top: 10px;
            border-top: 1px solid var(--panel-border);
        }

        .dot {
            width: 10px;
            height: 10px;
            border-radius: 50%;
            background-color: var(--accent-cold);
            box-shadow: 0 0 8px var(--accent-cold);
            transition: all 0.3s ease;
        }

        .note {
            font-size: 11px;
            color: #64748b;
            margin-top: 8px;
            font-style: italic;
        }

        /* Temp gauge styles */
        .gauge-container {
            display: flex;
            align-items: center;
            gap: 12px;
        }
        
        .gauge-bar {
            width: 150px;
            height: 6px;
            background: #1e293b;
            border-radius: 3px;
            position: relative;
            overflow: hidden;
        }

        .gauge-fill {
            position: absolute;
            left: 0; top: 0; bottom: 0;
            width: 0%;
            background: linear-gradient(90deg, var(--accent-cold), var(--accent-hot));
            border-radius: 3px;
        }

        .temp-text {
            font-size: 24px;
            font-weight: bold;
            color: var(--accent-cold);
            min-width: 70px;
            text-align: right;
            font-variant-numeric: tabular-nums;
        }
    </style>
</head>
<body>

    <div class="container">
        <!-- 面板信息 -->
        <div class="info-panel panel-top-left">
            <div class="title">最终理想解 (IFR) 设计</div>
            <div class="badge">TRIZ 理论应用</div>
            <div class="data-row">
                <span class="data-label">核心矛盾:</span>
                <span>需避让肩台(小尺寸) vs 需抗剪锁定(大尺寸)</span>
            </div>
            <div class="data-row">
                <span class="data-label">引入资源:</span>
                <span style="color: var(--accent-hot)">系统自身热能 (150℃ -> 300℃)</span>
            </div>
            <div class="data-row">
                <span class="data-label">理想解状态:</span>
                <span>无额外活动部件,依靠环境温度自动形变锁紧</span>
            </div>
        </div>

        <div class="info-panel panel-top-right">
            <div class="title">运行参数监控</div>
            <div class="gauge-container" style="margin: 15px 0;">
                <div class="gauge-bar">
                    <div class="gauge-fill" id="gauge-fill"></div>
                </div>
                <div class="temp-text" id="temp-val">150℃</div>
            </div>
            <div class="status-indicator">
                <div class="dot" id="status-dot"></div>
                <span id="status-text" style="color: var(--accent-cold);">收拢状态 (自由装拔)</span>
            </div>
        </div>

        <div class="info-panel panel-bottom-left">
            <div class="title">结构参数</div>
            <div class="data-row"><span class="data-label">部件构成:</span><span>6片双金属悬臂梁 (截面展示2片)</span></div>
            <div class="data-row"><span class="data-label">高膨胀层:</span><span style="color: #ef4444;">内侧红橙色区域</span></div>
            <div class="data-row"><span class="data-label">低膨胀层:</span><span style="color: #64748b;">外侧暗灰色区域</span></div>
            <div class="data-row"><span class="data-label">形变位移:</span><span>≥ 3.0 mm (视觉放大)</span></div>
            <div class="note">* 动画中横向位移特征已作适当视觉放大以清晰展示机理</div>
        </div>

        <div class="info-panel panel-bottom-right">
            <div class="title">动作时序</div>
            <div class="data-row" id="step-1" style="color: var(--accent-cold);">1. 150℃: 钩端收拢,无损拔插避让肩台</div>
            <div class="data-row" id="step-2" style="color: #f59e0b; opacity: 0.5;">2. 升温过程: 双金属层膨胀差导致悬臂梁外翻</div>
            <div class="data-row" id="step-3" style="color: var(--accent-hot); opacity: 0.5;">3. 300℃: 卡钩伸入肩台下方,形成抗剪锁紧</div>
        </div>

        <!-- 核心 SVG 动画区 -->
        <svg viewBox="0 0 800 600" preserveAspectRatio="xMidYMid meet">
            <defs>
                <!-- 材质渐变定义 -->
                <linearGradient id="metal-flange" x1="0%" y1="0%" x2="0%" y2="100%">
                    <stop offset="0%" stop-color="#475569" />
                    <stop offset="50%" stop-color="#334155" />
                    <stop offset="100%" stop-color="#1e293b" />
                </linearGradient>

                <linearGradient id="metal-shoulder" x1="0%" y1="0%" x2="100%" y2="0%">
                    <stop offset="0%" stop-color="#1e293b" />
                    <stop offset="100%" stop-color="#0f172a" />
                </linearGradient>
                
                <linearGradient id="metal-hook" x1="0%" y1="0%" x2="100%" y2="100%">
                    <stop offset="0%" stop-color="#94a3b8" />
                    <stop offset="100%" stop-color="#475569" />
                </linearGradient>

                <!-- 阴影带与纹理 -->
                <pattern id="hatch" width="8" height="8" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
                    <line x1="0" y1="0" x2="0" y2="8" stroke="rgba(255,255,255,0.03)" stroke-width="2" />
                </pattern>

                <filter id="glow-hot" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur stdDeviation="8" result="blur" />
                    <feComposite in="SourceGraphic" in2="blur" operator="over" />
                </filter>
            </defs>

            <!-- 静态背景基准线 -->
            <line x1="400" y1="50" x2="400" y2="550" stroke="rgba(255,255,255,0.1)" stroke-width="1" stroke-dasharray="5 5" />
            <text x="410" y="70" fill="rgba(255,255,255,0.3)" font-size="12">中心轴线 CL</text>

            <!-- 固定支座环形肩台 (左侧) -->
            <g id="left-shoulder">
                <path d="M 50,280 L 290,280 L 290,410 L 50,410 Z" fill="url(#metal-shoulder)" stroke="#334155" stroke-width="2" />
                <path d="M 50,280 L 290,280 L 290,410 L 50,410 Z" fill="url(#hatch)" />
                <rect x="290" y="280" width="4" height="130" fill="#64748b" /> <!-- 边缘高光 -->
                <text x="170" y="350" fill="rgba(255,255,255,0.2)" font-size="14" text-anchor="middle" letter-spacing="2">固定支座肩台</text>
            </g>

            <!-- 固定支座环形肩台 (右侧) -->
            <g id="right-shoulder">
                <path d="M 510,280 L 750,280 L 750,410 L 510,410 Z" fill="url(#metal-shoulder)" stroke="#334155" stroke-width="2" />
                <path d="M 510,280 L 750,280 L 750,410 L 510,410 Z" fill="url(#hatch)" />
                <rect x="506" y="280" width="4" height="130" fill="#64748b" /> <!-- 边缘高光 -->
                <text x="630" y="350" fill="rgba(255,255,255,0.2)" font-size="14" text-anchor="middle" letter-spacing="2">固定支座肩台</text>
            </g>

            <!-- 顶部法兰 (随中心件固定) -->
            <path d="M 220,100 L 580,100 L 580,160 L 220,160 Z" fill="url(#metal-flange)" stroke="#64748b" stroke-width="2" />
            <circle cx="260" cy="130" r="6" fill="#0f172a" stroke="#64748b" stroke-width="2" />
            <circle cx="330" cy="130" r="6" fill="#0f172a" stroke="#64748b" stroke-width="2" />
            <circle cx="470" cy="130" r="6" fill="#0f172a" stroke="#64748b" stroke-width="2" />
            <circle cx="540" cy="130" r="6" fill="#0f172a" stroke="#64748b" stroke-width="2" />
            <text x="400" y="135" fill="rgba(255,255,255,0.5)" font-size="16" text-anchor="middle" font-weight="bold" letter-spacing="4">反射体底法兰</text>

            <!-- 动态双金属悬臂梁 (左侧) -->
            <g id="left-beam-group">
                <!-- 低膨胀层 (外侧 - 灰) -->
                <path id="left-outer" fill="#475569" stroke="#1e293b" stroke-width="1" />
                <!-- 高膨胀层 发光背板 -->
                <path id="left-inner-glow" fill="#ff4713" filter="url(#glow-hot)" opacity="0" />
                <!-- 高膨胀层 (内侧 - 热敏) -->
                <path id="left-inner" fill="#94a3b8" stroke="#1e293b" stroke-width="1" />
                <!-- 刚性钩头 -->
                <g id="left-hook-head">
                    <path d="M 0,0 L 0,28 L -38,28 L -38,13 L -16,13 L -16,0 Z" fill="url(#metal-hook)" stroke="#1e293b" stroke-width="1" stroke-linejoin="round" />
                    <!-- 耐磨堆焊层标识 -->
                    <path d="M -38,28 L -16,28 L -16,13 L -38,13 Z" fill="none" stroke="#fbbf24" stroke-width="2" opacity="0.6"/>
                </g>
            </g>

            <!-- 动态双金属悬臂梁 (右侧) -->
            <g id="right-beam-group">
                <!-- 低膨胀层 (外侧 - 灰) -->
                <path id="right-outer" fill="#475569" stroke="#1e293b" stroke-width="1" />
                <!-- 高膨胀层 发光背板 -->
                <path id="right-inner-glow" fill="#ff4713" filter="url(#glow-hot)" opacity="0" />
                <!-- 高膨胀层 (内侧 - 热敏) -->
                <path id="right-inner" fill="#94a3b8" stroke="#1e293b" stroke-width="1" />
                <!-- 刚性钩头 -->
                <g id="right-hook-head">
                    <path d="M 0,0 L 0,28 L 38,28 L 38,13 L 16,13 L 16,0 Z" fill="url(#metal-hook)" stroke="#1e293b" stroke-width="1" stroke-linejoin="round" />
                    <!-- 耐磨堆焊层标识 -->
                    <path d="M 38,28 L 16,28 L 16,13 L 38,13 Z" fill="none" stroke="#fbbf24" stroke-width="2" opacity="0.6"/>
                </g>
            </g>
            
            <!-- 间隙与深度指示线 (动态透明度) -->
            <g id="dimension-lines" opacity="0">
                <line x1="264" y1="418" x2="290" y2="418" stroke="#ef4444" stroke-width="2" marker-start="url(#arrow)" marker-end="url(#arrow)" />
                <text x="277" y="435" fill="#ef4444" font-size="12" text-anchor="middle">5mm</text>
            </g>
        </svg>
    </div>

    <script>
        // 核心参数设定
        const FLANGE_Y = 160;
        const HOOK_Y_BASE = 390;
        const LEFT_BASE_X = 330;
        const RIGHT_BASE_X = 470;
        const BEAM_THICKNESS = 8; // 单层厚度
        const MAX_BEND_OFFSET = 32; // 放大后的横向弯曲位移
        const MAX_ANGLE = 14; // 底部钩头随动角度

        // DOM 元素引用
        const els = {
            leftInner: document.getElementById('left-inner'),
            leftOuter: document.getElementById('left-outer'),
            leftInnerGlow: document.getElementById('left-inner-glow'),
            leftHookHead: document.getElementById('left-hook-head'),
            
            rightInner: document.getElementById('right-inner'),
            rightOuter: document.getElementById('right-outer'),
            rightInnerGlow: document.getElementById('right-inner-glow'),
            rightHookHead: document.getElementById('right-hook-head'),
            
            tempVal: document.getElementById('temp-val'),
            gaugeFill: document.getElementById('gauge-fill'),
            statusDot: document.getElementById('status-dot'),
            statusText: document.getElementById('status-text'),
            dimLines: document.getElementById('dimension-lines'),
            
            step1: document.getElementById('step-1'),
            step2: document.getElementById('step-2'),
            step3: document.getElementById('step-3')
        };

        // 颜色插值函数 (150C -> 300C, 冷钢色 -> 炙热橙红)
        function interpolateColor(t) {
            // Cold: #94a3b8 (148, 163, 184)
            // Hot: #ff4713 (255, 71, 19)
            const r = Math.round(148 + (255 - 148) * t);
            const g = Math.round(163 + (71 - 163) * t);
            const b = Math.round(184 + (19 - 184) * t);
            return `rgb(${r}, ${g}, ${b})`;
        }

        // 动画更新主逻辑 (t: 0.0 ~ 1.0)
        function updateMechanism(t) {
            const temp = Math.round(150 + 150 * t);
            
            // 1. 更新 UI 面板
            els.tempVal.textContent = `${temp}℃`;
            els.gaugeFill.style.width = `${t * 100}%`;
            
            if (t < 0.1) {
                els.statusText.textContent = "收拢状态 (避让间隙 > 2mm)";
                els.statusText.style.color = "var(--accent-cold)";
                els.statusDot.style.backgroundColor = "var(--accent-cold)";
                els.statusDot.style.boxShadow = "0 0 8px var(--accent-cold)";
                els.dimLines.setAttribute('opacity', '0');
                
                els.step1.style.opacity = '1';
                els.step2.style.opacity = '0.4';
                els.step3.style.opacity = '0.4';
            } else if (t > 0.9) {
                els.statusText.textContent = "锁定状态 (抗剪钩入深度 5mm)";
                els.statusText.style.color = "var(--accent-hot)";
                els.statusDot.style.backgroundColor = "var(--accent-hot)";
                els.statusDot.style.boxShadow = "0 0 12px var(--accent-hot)";
                els.dimLines.setAttribute('opacity', '1');
                
                els.step1.style.opacity = '0.4';
                els.step2.style.opacity = '0.4';
                els.step3.style.opacity = '1';
            } else {
                els.statusText.textContent = "环境升温,自适应热变形驱动中...";
                els.statusText.style.color = "#f59e0b";
                els.statusDot.style.backgroundColor = "#f59e0b";
                els.statusDot.style.boxShadow = "0 0 8px #f59e0b";
                els.dimLines.setAttribute('opacity', t.toString());
                
                els.step1.style.opacity = '0.4';
                els.step2.style.opacity = '1';
                els.step3.style.opacity = '0.4';
            }

            // 2. 计算结构变形学参数
            // 左侧向左弯曲 (负向X位移),右侧向右弯曲 (正向X位移)
            const bendOffset = MAX_BEND_OFFSET * t;
            const bendAngle = MAX_ANGLE * t;
            
            // 左侧坐标系计算
            const lEndX = LEFT_BASE_X - bendOffset;
            const lCtrlX = LEFT_BASE_X - bendOffset * 0.45; // 贝塞尔控制点,营造悬臂梁弯曲感
            
            // 高膨胀层 (左悬臂梁内侧,靠右的一层)
            const lInnerPath = `M ${LEFT_BASE_X} ${FLANGE_Y} 
                                Q ${lCtrlX} 270 ${lEndX} ${HOOK_Y_BASE} 
                                L ${lEndX - BEAM_THICKNESS} ${HOOK_Y_BASE} 
                                Q ${lCtrlX - BEAM_THICKNESS} 270 ${LEFT_BASE_X - BEAM_THICKNESS} ${FLANGE_Y} Z`;
                                
            // 低膨胀层 (左悬臂梁外侧,靠左的一层)
            const lOuterPath = `M ${LEFT_BASE_X - BEAM_THICKNESS} ${FLANGE_Y} 
                                Q ${lCtrlX - BEAM_THICKNESS} 270 ${lEndX - BEAM_THICKNESS} ${HOOK_Y_BASE} 
                                L ${lEndX - BEAM_THICKNESS*2} ${HOOK_Y_BASE} 
                                Q ${lCtrlX - BEAM_THICKNESS*2} 270 ${LEFT_BASE_X - BEAM_THICKNESS*2} ${FLANGE_Y} Z`;

            // 右侧坐标系计算
            const rEndX = RIGHT_BASE_X + bendOffset;
            const rCtrlX = RIGHT_BASE_X + bendOffset * 0.45;

            // 高膨胀层 (右悬臂梁内侧,靠左的一层)
            const rInnerPath = `M ${RIGHT_BASE_X} ${FLANGE_Y} 
                                Q ${rCtrlX} 270 ${rEndX} ${HOOK_Y_BASE} 
                                L ${rEndX + BEAM_THICKNESS} ${HOOK_Y_BASE} 
                                Q ${rCtrlX + BEAM_THICKNESS} 270 ${RIGHT_BASE_X + BEAM_THICKNESS} ${FLANGE_Y} Z`;
                                
            // 低膨胀层 (右悬臂梁外侧,靠右的一层)
            const rOuterPath = `M ${RIGHT_BASE_X + BEAM_THICKNESS} ${FLANGE_Y} 
                                Q ${rCtrlX + BEAM_THICKNESS} 270 ${rEndX + BEAM_THICKNESS} ${HOOK_Y_BASE} 
                                L ${rEndX + BEAM_THICKNESS*2} ${HOOK_Y_BASE} 
                                Q ${rCtrlX + BEAM_THICKNESS*2} 270 ${RIGHT_BASE_X + BEAM_THICKNESS*2} ${FLANGE_Y} Z`;

            // 3. 应用路径与变换
            els.leftInner.setAttribute('d', lInnerPath);
            els.leftInnerGlow.setAttribute('d', lInnerPath);
            els.leftOuter.setAttribute('d', lOuterPath);
            els.leftHookHead.setAttribute('transform', `translate(${lEndX}, ${HOOK_Y_BASE}) rotate(${bendAngle})`);

            els.rightInner.setAttribute('d', rInnerPath);
            els.rightInnerGlow.setAttribute('d', rInnerPath);
            els.rightOuter.setAttribute('d', rOuterPath);
            els.rightHookHead.setAttribute('transform', `translate(${rEndX}, ${HOOK_Y_BASE}) rotate(${-bendAngle})`);

            // 4. 应用颜色与发光特效
            const heatColor = interpolateColor(t);
            els.leftInner.setAttribute('fill', heatColor);
            els.rightInner.setAttribute('fill', heatColor);
            
            // 发光层透明度与温度强相关
            const glowOpacity = Math.max(0, (t - 0.2) * 1.25); 
            els.leftInnerGlow.setAttribute('opacity', glowOpacity.toString());
            els.rightInnerGlow.setAttribute('opacity', glowOpacity.toString());
        }

        // 动画时间轴控制
        let startTime = null;
        const CYCLE_DURATION = 10000; // 完整的循环周期:10秒

        function animationLoop(timestamp) {
            if (!startTime) startTime = timestamp;
            const elapsed = timestamp - startTime;
            const phase = (elapsed % CYCLE_DURATION) / CYCLE_DURATION;
            
            let t = 0;
            // 阶段切分:
            // 0.0 - 0.1: 保持 150℃ (t=0)
            // 0.1 - 0.4: 升温至 300℃ (t 0->1)
            // 0.4 - 0.6: 保持 300℃ (t=1)
            // 0.6 - 0.9: 降温至 150℃ (t 1->0)
            // 0.9 - 1.0: 保持 150℃ (t=0)

            if (phase < 0.1) {
                t = 0;
            } else if (phase < 0.4) {
                // 平滑缓动升温
                const progress = (phase - 0.1) / 0.3;
                t = 0.5 - Math.cos(progress * Math.PI) / 2;
            } else if (phase < 0.6) {
                t = 1;
            } else if (phase < 0.9) {
                // 平滑缓动降温
                const progress = (phase - 0.6) / 0.3;
                t = 0.5 + Math.cos(progress * Math.PI) / 2;
            } else {
                t = 0;
            }

            updateMechanism(t);
            requestAnimationFrame(animationLoop);
        }

        // 页面加载完成后自动触发动画,重开即播
        window.addEventListener('DOMContentLoaded', () => {
            requestAnimationFrame(animationLoop);
        });
    </script>
</body>
</html>
积分规则:每轮对话扣10分