分享图
动画工坊
引擎就绪
<!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 {
            /* 极简工业暗黑美学 / Blueprint / Precision */
            --bg-base: #050505;
            --bg-grid: #171717;
            --panel-border: #27272a;
            --text-main: #a1a1aa;
            --text-dim: #52525b;
            --text-bright: #f4f4f5;
            
            /* 核心标识色 */
            --color-ifr-cyan: #0ea5e9;  /* 理想解:垂直承重墙 */
            --color-force-amber: #f59e0b; /* 作用力:磨削力 */
            --color-spring: #fbbf24;      /* 工件:弹簧 */
            --color-disk: #27272a;        /* 基体:磨盘 */
            
            --font-mono: 'JetBrains Mono', 'Consolas', 'Courier New', monospace;
            --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
        }

        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            background-color: var(--bg-base);
            color: var(--text-main);
            font-family: var(--font-sans);
            height: 100vh;
            display: flex;
            flex-direction: column;
            overflow: hidden;
            /* 工业网格背景 */
            background-image: 
                linear-gradient(var(--bg-grid) 1px, transparent 1px),
                linear-gradient(90deg, var(--bg-grid) 1px, transparent 1px);
            background-size: 40px 40px;
            background-position: center center;
        }

        /* 绝对避免遮挡中央动画的边缘排版 */
        header {
            position: absolute;
            top: 24px;
            left: 32px;
            z-index: 10;
            pointer-events: none;
        }

        .sys-title {
            font-family: var(--font-mono);
            font-size: 14px;
            color: var(--text-bright);
            letter-spacing: 1px;
            font-weight: 600;
            margin-bottom: 8px;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .sys-title::before {
            content: '';
            display: inline-block;
            width: 8px;
            height: 8px;
            background-color: var(--color-ifr-cyan);
            border-radius: 50%;
            box-shadow: 0 0 8px var(--color-ifr-cyan);
        }

        .sys-desc {
            font-size: 12px;
            max-width: 320px;
            line-height: 1.6;
            color: var(--text-dim);
        }

        .telemetry {
            position: absolute;
            top: 24px;
            right: 32px;
            text-align: right;
            font-family: var(--font-mono);
            font-size: 12px;
            color: var(--text-dim);
            z-index: 10;
        }

        .telemetry .status-indicator {
            color: var(--color-ifr-cyan);
            animation: pulse 2s infinite;
        }

        .footer-info {
            position: absolute;
            bottom: 24px;
            left: 32px;
            right: 32px;
            display: flex;
            justify-content: space-between;
            font-family: var(--font-mono);
            font-size: 11px;
            color: var(--text-dim);
            border-top: 1px solid var(--panel-border);
            padding-top: 12px;
            z-index: 10;
        }

        /* 中央视口,最大化核心动画空间 */
        .stage {
            flex: 1;
            display: flex;
            justify-content: center;
            align-items: center;
            position: relative;
            width: 100%;
            height: 100%;
        }

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

        @keyframes pulse {
            0%, 100% { opacity: 0.5; }
            50% { opacity: 1; }
        }

        /* 隐藏滚动条 */
        ::-webkit-scrollbar { display: none; }
    </style>
</head>
<body>

    <header>
        <div class="sys-title">TRIZ_IFR: 偏心阶梯孔(斜度锥孔)强制导向模型</div>
        <div class="sys-desc">
            消除“点头/翘尾”效应。<br>
            直接利用磨削力作为驱动源,将弹簧推向高精度半圆柱面(受力侧2.02mm)。非受力侧保留0.15~0.2mm避空,实现零斜率垂直贴合。
        </div>
    </header>

    <div class="telemetry">
        <div class="status-indicator">● RUNNING</div>
        <div>T_CYCLE: 6000ms</div>
        <div id="phase-readout">PHASE: STANDBY</div>
    </div>

    <main class="stage">
        <!-- 主SVG视口,基于 viewBox 确保自适应与极佳空间布局 -->
        <svg id="canvas" viewBox="0 0 1200 600" preserveAspectRatio="xMidYMid meet">
            <defs>
                <!-- 滤镜定义:高精度墙面发光 (理想解) -->
                <filter id="glowCyan" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur in="SourceGraphic" stdDeviation="3" result="blur1" />
                    <feGaussianBlur in="SourceGraphic" stdDeviation="8" result="blur2" />
                    <feMerge>
                        <feMergeNode in="blur2" />
                        <feMergeNode in="blur1" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>
                
                <!-- 滤镜定义:作用力发光 -->
                <filter id="glowAmber" x="-50%" y="-50%" width="200%" height="200%">
                    <feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blur1" />
                    <feMerge>
                        <feMergeNode in="blur1" />
                        <feMergeNode in="SourceGraphic" />
                    </feMerge>
                </filter>

                <!-- 材料剖面填充纹理 (磨盘基体) -->
                <pattern id="hatch" width="8" height="8" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
                    <line x1="0" y1="0" x2="0" y2="8" stroke="#18181b" stroke-width="1.5" />
                </pattern>

                <marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
                    <path d="M 0 0 L 10 5 L 0 10 z" fill="var(--color-force-amber)" />
                </marker>
            </defs>

            <!-- ============================================== -->
            <!-- 左侧面板:X-Y 俯视图 (偏心葫芦形孔)             -->
            <!-- ============================================== -->
            <g transform="translate(300, 300)" id="top-view">
                <!-- 坐标系辅助线 -->
                <line x1="-150" y1="0" x2="150" y2="0" stroke="#27272a" stroke-width="1" stroke-dasharray="4 4" />
                <line x1="0" y1="-150" x2="0" y2="150" stroke="#27272a" stroke-width="1" stroke-dasharray="4 4" />
                
                <!-- 视图标签 -->
                <text x="-150" y="-130" fill="var(--text-dim)" font-family="var(--font-mono)" font-size="12">TOP VIEW (X-Y Plane)</text>
                <text x="-150" y="-110" fill="var(--text-dim)" font-family="var(--font-mono)" font-size="10">Scale: ~1:40 (Exaggerated for clarity)</text>

                <!-- 磨盘基体外缘 -->
                <circle cx="0" cy="0" r="140" fill="transparent" stroke="#27272a" stroke-width="2" />
                
                <!-- 偏心葫芦形孔底色 -->
                <path d="M -30,44 A 44,44 0 1,0 30,44 L 25,-40.4 A 40.4,40.4 0 1,1 -25,-40.4 Z" fill="#09090b" stroke="#3f3f46" stroke-width="2" />
                
                <!-- 尺寸标注 -->
                <text x="60" y="70" fill="#52525b" font-family="var(--font-mono)" font-size="11">Ø2.20mm (下料侧)</text>
                <text x="60" y="-50" fill="var(--color-ifr-cyan)" font-family="var(--font-mono)" font-size="11">Ø2.02mm (高精度承重侧)</text>

                <!-- 核心理想解:高精度半圆面高亮 (初始隐藏) -->
                <path id="top-wall-glow" d="M -40.4,-40.4 A 40.4,40.4 0 0,1 40.4,-40.4" fill="transparent" stroke="var(--color-ifr-cyan)" stroke-width="3" filter="url(#glowCyan)" opacity="0" />

                <!-- 工件(弹簧) 俯视 -->
                <g id="top-spring">
                    <circle cx="0" cy="0" r="40" fill="#27272a" stroke="var(--color-spring)" stroke-width="2" />
                    <!-- 弹簧内部结构示意 -->
                    <circle cx="0" cy="0" r="30" fill="transparent" stroke="var(--color-spring)" stroke-width="0.5" stroke-dasharray="2 2" />
                    <circle cx="0" cy="0" r="20" fill="transparent" stroke="var(--color-spring)" stroke-width="0.5" stroke-dasharray="2 2" />
                </g>

                <!-- 磨削力指示箭头 (俯视) -->
                <g id="top-force" opacity="0">
                    <line x1="0" y1="90" x2="0" y2="45" stroke="var(--color-force-amber)" stroke-width="3" marker-end="url(#arrow)" filter="url(#glowAmber)" />
                    <text x="10" y="85" fill="var(--color-force-amber)" font-family="var(--font-mono)" font-size="11" filter="url(#glowAmber)">F_grind</text>
                </g>
            </g>

            <!-- 分隔线 -->
            <line x1="600" y1="100" x2="600" y2="500" stroke="#27272a" stroke-width="1" stroke-dasharray="4 4" />

            <!-- ============================================== -->
            <!-- 右侧面板:Z轴剖视图 (垂直度强制导向)           -->
            <!-- ============================================== -->
            <g transform="translate(900, 300)" id="side-view">
                <!-- 坐标系辅助线 -->
                <line x1="-200" y1="160" x2="200" y2="160" stroke="#27272a" stroke-width="1" stroke-dasharray="4 4" /> <!-- 底部基准 -->
                
                <text x="-250" y="-200" fill="var(--text-dim)" font-family="var(--font-mono)" font-size="12">SECTION VIEW (Y-Z Plane)</text>
                
                <!-- 磨盘基体 (厚度8mm映射为320px) -->
                <!-- 左侧基体 (避空侧, 较宽) -->
                <rect x="-150" y="-160" width="106" height="320" fill="url(#hatch)" stroke="#3f3f46" stroke-width="2" />
                <!-- 右侧基体 (高精度承重墙) -->
                <rect x="40.4" y="-160" width="109.6" height="320" fill="url(#hatch)" stroke="#3f3f46" stroke-width="2" />
                
                <!-- 剖视图孔的轮廓线 -->
                <line x1="-44" y1="-160" x2="-44" y2="160" stroke="#52525b" stroke-width="1.5" /> <!-- 左壁 (大孔边界) -->
                <line x1="40.4" y1="-160" x2="40.4" y2="160" stroke="#52525b" stroke-width="1.5" /> <!-- 右壁 (绝对垂直面) -->

                <!-- 核心理想解:垂直承重墙高亮 -->
                <line id="side-wall-glow" x1="40.4" y1="-160" x2="40.4" y2="160" stroke="var(--color-ifr-cyan)" stroke-width="4" filter="url(#glowCyan)" opacity="0" />
                <text id="side-wall-text" x="60" y="0" fill="var(--color-ifr-cyan)" font-family="var(--font-sans)" font-size="12" font-weight="bold" filter="url(#glowCyan)" opacity="0">绝对垂直承重墙 (100%贴合)</text>
                <text id="side-gap-text" x="-140" y="0" fill="var(--text-dim)" font-family="var(--font-mono)" font-size="11" opacity="0">避空余量: 0.15mm</text>

                <!-- 工件(弹簧) 侧视 -->
                <g id="side-spring">
                    <!-- 弹簧外轮廓包络线 -->
                    <rect x="-40" y="-160" width="80" height="320" fill="#18181b" stroke="var(--color-spring)" stroke-width="1.5" opacity="0.8" />
                    <!-- 内部波浪线模拟弹簧绕线 -->
                    <path d="M -40,-150 L 40,-130 L -40,-110 L 40,-90 L -40,-70 L 40,-50 L -40,-30 L 40,-10 L -40,10 L 40,30 L -40,50 L 40,70 L -40,90 L 40,110 L -40,130 L 40,150" fill="transparent" stroke="var(--color-spring)" stroke-width="2" opacity="0.6" />
                </g>

                <!-- 砂轮与磨削力 (侧视) -->
                <g id="grinding-wheel" transform="translate(0, -220)">
                    <rect x="-100" y="-40" width="200" height="40" fill="#3f3f46" rx="5" />
                    <rect x="-100" y="0" width="200" height="10" fill="var(--color-force-amber)" />
                    <text x="110" y="-15" fill="var(--text-dim)" font-family="var(--font-mono)" font-size="11">砂轮 (Grinding Wheel)</text>
                </g>

                <!-- 磨削力箭头 (侧视) -->
                <g id="side-force" opacity="0">
                    <line x1="-80" y1="-120" x2="-45" y2="-120" stroke="var(--color-force-amber)" stroke-width="3" marker-end="url(#arrow)" filter="url(#glowAmber)" />
                    <text x="-120" y="-116" fill="var(--color-force-amber)" font-family="var(--font-mono)" font-size="11" filter="url(#glowAmber)">推力</text>
                </g>

                <!-- 磨削火花层 (通过JS动态生成) -->
                <g id="sparks"></g>
            </g>
        </svg>
    </main>

    <div class="footer-info">
        <div>[TRIZ_PRINCIPLE] 资源利用 / 局部质量 (最终理想解)</div>
        <div>ENGINE: VanillaJS / CSS / SVG_SMIL</div>
    </div>

    <script>
        /**
         * 绝对安全:无外部依赖,纯原生实现的核心动画逻辑。
         * 基于 requestAnimationFrame 的无缝状态机,保证"重开即播"及"自动循环"
         */
        const TOTAL_DURATION = 6000; // 6秒一个循环
        
        // DOM 元素引用
        const topSpring = document.getElementById('top-spring');
        const topForce = document.getElementById('top-force');
        const topWallGlow = document.getElementById('top-wall-glow');
        
        const sideSpring = document.getElementById('side-spring');
        const sideForce = document.getElementById('side-force');
        const sideWallGlow = document.getElementById('side-wall-glow');
        const sideWallText = document.getElementById('side-wall-text');
        const sideGapText = document.getElementById('side-gap-text');
        
        const grindingWheel = document.getElementById('grinding-wheel');
        const sparksContainer = document.getElementById('sparks');
        const phaseReadout = document.getElementById('phase-readout');

        // 插值函数
        const lerp = (start, end, t) => start + (end - start) * t;
        const easeInOut = t => t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t;

        // 火花池管理
        const sparkPool = [];
        for(let i=0; i<15; i++) {
            const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
            line.setAttribute("stroke", "var(--color-force-amber)");
            line.setAttribute("stroke-width", Math.random() * 1.5 + 0.5);
            line.setAttribute("opacity", "0");
            sparksContainer.appendChild(line);
            sparkPool.push({
                el: line,
                x: 0, y: 0, vx: 0, vy: 0, life: 0
            });
        }

        function emitSparks(intensity) {
            sparkPool.forEach(spark => {
                if(spark.life <= 0 && Math.random() < intensity) {
                    spark.x = (Math.random() * 60) - 20; // 顶部偏左区域发生切削
                    spark.y = -160;
                    spark.vx = (Math.random() * 8 + 2); // 向右飞溅
                    spark.vy = -(Math.random() * 6 + 2); // 向上飞溅
                    spark.life = 1.0;
                }
                
                if(spark.life > 0) {
                    spark.x += spark.vx;
                    spark.y += spark.vy;
                    spark.vy += 0.2; // 重力
                    spark.life -= 0.05;
                    
                    spark.el.setAttribute("x1", spark.x);
                    spark.el.setAttribute("y1", spark.y);
                    spark.el.setAttribute("x2", spark.x - spark.vx);
                    spark.el.setAttribute("y2", spark.y - spark.vy);
                    spark.el.setAttribute("opacity", spark.life);
                } else {
                    spark.el.setAttribute("opacity", "0");
                }
            });
        }

        function clearSparks() {
            sparkPool.forEach(spark => {
                spark.life = 0;
                spark.el.setAttribute("opacity", "0");
            });
        }

        // 核心渲染循环
        function render(timestamp) {
            const time = timestamp % TOTAL_DURATION;
            const progress = time / TOTAL_DURATION; // 0.0 到 1.0
            
            // 状态机变量
            let topSpringY = 0;
            let sideSpringY = 0;
            let sideSpringX = 0;
            let sideSpringTilt = 0; // 点头/翘尾角度
            
            let wheelY = -280;
            let forceOpacity = 0;
            let glowOpacity = 0;
            let sparkIntensity = 0;
            let phaseText = "";

            /* 
             * 时间轴分配:
             * 0.0 - 0.1: 下料入孔 (Drop In)
             * 0.1 - 0.2: 砂轮接近,施加磨削力 (Engage)
             * 0.2 - 0.3: 【核心动作】工件被推至垂直壁面,完全贴合并强制垂直 (Lock/IFR)
             * 0.3 - 0.7: 磨削过程,展示稳定性 (Grinding)
             * 0.7 - 0.8: 砂轮移开,力消失,工件松脱 (Release)
             * 0.8 - 1.0: 移出 (Eject & Reset)
             */

            if (progress < 0.1) {
                // 阶段1:落料
                phaseText = "PHASE 1: 工件落入 Ø2.2 偏心大孔";
                const p = progress / 0.1;
                topSpringY = lerp(44, 44, p); // 初始在大孔中心
                sideSpringY = lerp(-300, 0, easeInOut(p));
                sideSpringX = -4; // 靠左侧大孔
                sideSpringTilt = lerp(5, 2, p); // 初始无约束,存在微小倾斜
            } 
            else if (progress < 0.2) {
                // 阶段2:受力
                phaseText = "PHASE 2: 砂轮切入,产生侧向切削力";
                const p = (progress - 0.1) / 0.1;
                topSpringY = 44;
                sideSpringY = 0;
                sideSpringX = -4;
                sideSpringTilt = 2;
                
                wheelY = lerp(-280, -170, easeInOut(p));
                forceOpacity = lerp(0, 1, p);
            }
            else if (progress < 0.3) {
                // 阶段3:理想解展现 (强制导向贴合)
                phaseText = "PHASE 3: [IFR生效] 强制垂直面 100% 贴合,消除倾斜";
                const p = (progress - 0.2) / 0.1;
                
                // 俯视:向小孔偏移 (中心Y从44移至-40.4)
                topSpringY = lerp(44, -40.4, easeInOut(p));
                
                // 侧视:向右壁移动并消除倾斜
                sideSpringY = 0;
                sideSpringX = lerp(-4, 0.4, easeInOut(p)); // 0.4 是紧贴右壁的相对坐标
                sideSpringTilt = lerp(2, 0, easeInOut(p));
                
                wheelY = -170;
                forceOpacity = 1;
                glowOpacity = lerp(0, 1, p); // 承重墙高亮亮起
            }
            else if (progress < 0.7) {
                // 阶段4:稳定磨削
                phaseText = "PHASE 4: 零斜率强制导向磨削中...";
                topSpringY = -40.4;
                sideSpringY = 0;
                sideSpringX = 0.4;
                sideSpringTilt = 0; // 绝对垂直
                
                wheelY = -170;
                forceOpacity = 1;
                
                // 呼吸灯效果展示理想解
                glowOpacity = 0.8 + 0.2 * Math.sin(time * 0.01);
                sparkIntensity = 0.5;
            }
            else if (progress < 0.8) {
                // 阶段5:松脱
                phaseText = "PHASE 5: 加工结束,侧向力解除,弹簧松脱";
                const p = (progress - 0.7) / 0.1;
                
                topSpringY = lerp(-40.4, 44, easeInOut(p));
                sideSpringY = 0;
                sideSpringX = lerp(0.4, -4, easeInOut(p));
                sideSpringTilt = lerp(0, 2, easeInOut(p));
                
                wheelY = lerp(-170, -280, easeInOut(p));
                forceOpacity = lerp(1, 0, p);
                glowOpacity = lerp(1, 0, p);
                sparkIntensity = 0;
            }
            else {
                // 阶段6:复位
                phaseText = "PHASE 6: 循环复位";
                const p = (progress - 0.8) / 0.2;
                
                topSpringY = 44;
                sideSpringX = -4;
                sideSpringTilt = 2;
                sideSpringY = lerp(0, 300, easeInOut(p)); // 向下脱落
                
                wheelY = -280;
                forceOpacity = 0;
                glowOpacity = 0;
                sparkIntensity = 0;
            }

            // --- 实际应用属性 ---
            
            // 俯视图更新
            topSpring.setAttribute('transform', `translate(0, ${topSpringY})`);
            topForce.setAttribute('opacity', forceOpacity);
            topWallGlow.setAttribute('opacity', glowOpacity);
            
            // 剖视图更新
            // 使用 transform-origin 为中心底部附近以模拟摇头/翘尾
            sideSpring.setAttribute('transform', `translate(${sideSpringX}, ${sideSpringY}) rotate(${sideSpringTilt})`);
            sideSpring.style.transformOrigin = '0px 0px'; // 相对于侧视工件自身的原点
            
            grindingWheel.setAttribute('transform', `translate(0, ${wheelY})`);
            sideForce.setAttribute('opacity', forceOpacity);
            
            // 理想解文本与高亮
            sideWallGlow.setAttribute('opacity', glowOpacity);
            sideWallText.setAttribute('opacity', glowOpacity);
            sideGapText.setAttribute('opacity', glowOpacity);
            
            // 火花生成
            if (sparkIntensity > 0) {
                emitSparks(sparkIntensity);
            } else {
                clearSparks();
            }

            // 更新仪表盘文字
            phaseReadout.textContent = phaseText;

            // 持续循环
            requestAnimationFrame(render);
        }

        // DOM 加载完成后自动无缝触发,无需用户点击
        window.addEventListener('DOMContentLoaded', () => {
            requestAnimationFrame(render);
        });
    </script>
</body>
</html>
积分规则:第一轮对话扣减8分,后续每轮扣6分