分享图
动画工坊
引擎就绪
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>行星轮爬楼与主动悬挂解耦原理图 (IFR)</title>
    <style>
        :root {
            --bg-color: #060B14;
            --grid-color: #121D30;
            --stair-fill: #152238;
            --stair-stroke: #2E4773;
            --chassis-fill: #1A202C;
            --chassis-stroke: #4A5568;
            --wheel-carrier: #FF4B2B;
            --wheel-small: #718096;
            --actuator-body: #2D3748;
            --actuator-piston: #00E5FF;
            --platform: #FFFFFF;
            --text-glow: #00E5FF;
            --accent-orange: #FF4B2B;
            --font-mono: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
        }

        body, html {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            background-color: var(--bg-color);
            color: #fff;
            font-family: 'Inter', system-ui, sans-serif;
            overflow: hidden;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        #animation-container {
            width: 100%;
            height: 100%;
            max-width: 1920px;
            max-height: 1080px;
            position: relative;
        }

        svg {
            width: 100%;
            height: 100%;
            display: block;
        }

        .hud-overlay {
            position: absolute;
            top: 40px;
            left: 40px;
            pointer-events: none;
            z-index: 10;
        }

        .hud-title {
            font-size: 24px;
            font-weight: 800;
            letter-spacing: 2px;
            margin-bottom: 10px;
            text-transform: uppercase;
            background: linear-gradient(90deg, #fff, #888);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }

        .hud-subtitle {
            font-family: var(--font-mono);
            font-size: 14px;
            color: var(--text-glow);
            letter-spacing: 1px;
            margin-bottom: 20px;
            opacity: 0.8;
        }

        .hud-panel {
            background: rgba(10, 15, 25, 0.8);
            border: 1px solid rgba(0, 229, 255, 0.3);
            border-left: 3px solid var(--text-glow);
            padding: 15px 20px;
            font-family: var(--font-mono);
            backdrop-filter: blur(4px);
            margin-bottom: 15px;
            width: fit-content;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
        }

        .hud-row {
            display: flex;
            justify-content: space-between;
            align-items: center;
            gap: 30px;
            margin: 8px 0;
            font-size: 13px;
        }

        .hud-label {
            color: #8892b0;
            text-transform: uppercase;
        }

        .hud-value {
            color: #fff;
            font-weight: bold;
            font-size: 15px;
            text-align: right;
            min-width: 80px;
        }

        .val-cyan { color: var(--text-glow); text-shadow: 0 0 8px rgba(0, 229, 255, 0.5); }
        .val-orange { color: var(--accent-orange); text-shadow: 0 0 8px rgba(255, 75, 43, 0.5); }
        .val-green { color: #00FF66; text-shadow: 0 0 8px rgba(0, 255, 102, 0.5); }

        /* SVG Inner Styling */
        .grid-line { stroke: var(--grid-color); stroke-width: 1; }
        .stair-path { fill: var(--stair-fill); stroke: var(--stair-stroke); stroke-width: 3; stroke-linejoin: round; }
        .chassis { fill: var(--chassis-fill); stroke: var(--chassis-stroke); stroke-width: 4; stroke-linejoin: round; }
        .carrier-arm { stroke: var(--wheel-carrier); stroke-width: 12; stroke-linecap: round; }
        .wheel { fill: var(--wheel-small); stroke: #111; stroke-width: 3; }
        .wheel-hub { fill: #111; stroke: var(--wheel-carrier); stroke-width: 2; }
        .actuator-tube { stroke: var(--actuator-body); stroke-width: 18; stroke-linecap: round; }
        .actuator-rod { stroke: var(--actuator-piston); stroke-width: 8; stroke-linecap: round; }
        .platform-base { fill: #222; stroke: #444; stroke-width: 2; }
        .platform-top { fill: var(--platform); stroke: var(--platform); stroke-width: 2; }
        .imu-sensor { fill: #000; stroke: var(--text-glow); stroke-width: 2; }
        .glow-line { stroke: var(--text-glow); stroke-width: 2; stroke-dasharray: 4 4; opacity: 0.5; }
        
        @keyframes pulse {
            0% { opacity: 0.6; }
            50% { opacity: 1; filter: drop-shadow(0 0 5px var(--text-glow)); }
            100% { opacity: 0.6; }
        }
        .active-indicator { animation: pulse 1s infinite; }
    </style>
</head>
<body>

<div id="animation-container">
    <div class="hud-overlay">
        <div class="hud-title">Ideal Final Result (IFR)</div>
        <div class="hud-subtitle">ACTIVE SUSPENSION DECOUPLING SYSTEM</div>
        
        <div class="hud-panel">
            <div style="color: #fff; margin-bottom: 10px; border-bottom: 1px solid #333; padding-bottom: 5px; font-weight: bold;">[ IMU & PAYLOAD TELEMETRY ]</div>
            <div class="hud-row">
                <span class="hud-label">Chassis Pitch:</span>
                <span class="hud-value val-orange" id="hud-chassis-angle">0.00°</span>
            </div>
            <div class="hud-row">
                <span class="hud-label">Payload Pitch (IFR):</span>
                <span class="hud-value val-green">0.00° ABS</span>
            </div>
            <div class="hud-row">
                <span class="hud-label">IMU Response Time:</span>
                <span class="hud-value val-cyan" id="hud-response-time">14 ms</span>
            </div>
        </div>

        <div class="hud-panel">
            <div style="color: #fff; margin-bottom: 10px; border-bottom: 1px solid #333; padding-bottom: 5px; font-weight: bold;">[ ACTUATOR KINEMATICS ]</div>
            <div class="hud-row">
                <span class="hud-label">Front Stroke:</span>
                <span class="hud-value val-cyan" id="hud-front-stroke">0.0 mm</span>
            </div>
            <div class="hud-row">
                <span class="hud-label">Rear Stroke:</span>
                <span class="hud-value val-cyan" id="hud-rear-stroke">0.0 mm</span>
            </div>
            <div class="hud-row">
                <span class="hud-label">Carrier Arm (r):</span>
                <span class="hud-value">180 mm</span>
            </div>
        </div>
    </div>

    <svg id="scene" viewBox="0 0 1600 900" preserveAspectRatio="xMidYMid slice">
        <defs>
            <pattern id="grid" width="100" height="100" patternUnits="userSpaceOnUse">
                <path d="M 100 0 L 0 0 0 100" fill="none" class="grid-line"/>
                <path d="M 50 0 L 50 100 M 0 50 L 100 50" fill="none" class="grid-line" style="stroke-opacity: 0.3"/>
            </pattern>
            
            <filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
                <feGaussianBlur stdDeviation="6" result="blur" />
                <feComposite in="SourceGraphic" in2="blur" operator="over" />
            </filter>
            
            <filter id="glow-orange" x="-20%" y="-20%" width="140%" height="140%">
                <feGaussianBlur stdDeviation="8" result="blur" />
                <feComponentTransfer in="blur" result="glow">
                    <feFuncA type="linear" slope="1.5"/>
                </feComponentTransfer>
                <feMerge>
                    <feMergeNode in="glow"/>
                    <feMergeNode in="SourceGraphic"/>
                </feMerge>
            </filter>

            <!-- 四星轮系定义 -->
            <g id="planetary-wheel">
                <!-- 行星架十字交叉 -->
                <line x1="-50" y1="0" x2="50" y2="0" class="carrier-arm"/>
                <line x1="0" y1="-50" x2="0" y2="50" class="carrier-arm"/>
                
                <!-- 四个小轮 -->
                <circle cx="-50" cy="0" r="16" class="wheel"/>
                <circle cx="50" cy="0" r="16" class="wheel"/>
                <circle cx="0" cy="-50" r="16" class="wheel"/>
                <circle cx="0" cy="50" r="16" class="wheel"/>
                
                <!-- 中心轴承 -->
                <circle cx="0" cy="0" r="10" class="wheel-hub"/>
            </g>
        </defs>

        <!-- 背景网格 -->
        <rect width="100%" height="100%" fill="url(#grid)" />

        <!-- 场景组 (用于居中和缩放) -->
        <g transform="translate(800, 550)">
            
            <!-- 动态地形 (台阶) -->
            <g id="terrain-group"></g>

            <!-- 机器人主体 -->
            <g id="robot-group">
                
                <!-- 辅助对齐线 (展示IFR解耦原理) -->
                <line id="horizontal-reference" x1="-400" y1="-220" x2="400" y2="-220" class="glow-line" />
                <text x="410" y="-215" fill="var(--text-glow)" font-family="var(--font-mono)" font-size="14" opacity="0.8">ABSOLUTE HORIZONTAL PLANE</text>

                <!-- 底盘与车轮组 (受地形影响产生颠簸和倾斜) -->
                <g id="chassis-assembly">
                    <!-- 底盘本体 -->
                    <rect x="-240" y="-20" width="480" height="40" rx="10" class="chassis"/>
                    
                    <!-- IMU传感器 -->
                    <rect x="-20" y="-35" width="40" height="15" rx="3" class="imu-sensor" filter="url(#glow-cyan)"/>
                    <circle cx="0" cy="-27.5" r="3" fill="#00E5FF"/>
                    
                    <!-- 前行星轮组 -->
                    <g id="front-wheel" transform="translate(180, 0)">
                        <use href="#planetary-wheel" />
                        <circle cx="0" cy="0" r="4" fill="#fff"/>
                    </g>
                    
                    <!-- 后行星轮组 -->
                    <g id="rear-wheel" transform="translate(-180, 0)">
                        <use href="#planetary-wheel" />
                        <circle cx="0" cy="0" r="4" fill="#fff"/>
                    </g>
                </g>

                <!-- 主动悬挂推杆 (连接底盘和载货平台) -->
                <!-- 后推杆 -->
                <g id="actuator-rear-group">
                    <line id="actuator-rear-body" class="actuator-tube" x1="-150" y1="0" x2="-150" y2="-100"/>
                    <line id="actuator-rear-rod" class="actuator-rod" filter="url(#glow-cyan)" x1="-150" y1="-100" x2="-150" y2="-200"/>
                    <circle id="joint-rear-chassis" cx="-150" cy="0" r="6" fill="#8892b0"/>
                    <circle id="joint-rear-platform" cx="-150" cy="-200" r="6" fill="#8892b0"/>
                </g>

                <!-- 前推杆 -->
                <g id="actuator-front-group">
                    <line id="actuator-front-body" class="actuator-tube" x1="150" y1="0" x2="150" y2="-100"/>
                    <line id="actuator-front-rod" class="actuator-rod" filter="url(#glow-cyan)" x1="150" y1="-100" x2="150" y2="-200"/>
                    <circle id="joint-front-chassis" cx="150" cy="0" r="6" fill="#8892b0"/>
                    <circle id="joint-front-platform" cx="150" cy="-200" r="6" fill="#8892b0"/>
                </g>

                <!-- 载货平台 (IFR目标:永远保持绝对水平) -->
                <g id="payload-platform" transform="translate(0, -220)">
                    <rect x="-260" y="-10" width="520" height="20" rx="4" class="platform-base"/>
                    <rect x="-250" y="-25" width="500" height="15" rx="2" class="platform-top" filter="url(#glow-cyan)"/>
                    
                    <!-- 货物示意 -->
                    <rect x="-80" y="-85" width="160" height="60" rx="2" fill="none" stroke="#00E5FF" stroke-width="2" stroke-dasharray="6 4" opacity="0.6"/>
                    <path d="M-60 -45 L60 -45 M0 -75 L0 -25" stroke="#00E5FF" stroke-width="1" opacity="0.3"/>
                    <circle cx="0" cy="-55" r="15" fill="none" stroke="#00E5FF" stroke-width="2" opacity="0.5"/>
                    <text x="0" y="-51" fill="#00E5FF" font-family="var(--font-mono)" font-size="12" text-anchor="middle" opacity="0.8">CG</text>
                </g>

                <!-- 数据连线展示 -->
                <path id="imu-data-line" d="" fill="none" stroke="#00E5FF" stroke-width="2" stroke-dasharray="4 4" opacity="0.6" filter="url(#glow-cyan)"/>
            </g>
        </g>
    </svg>
</div>

<script>
    // 物理与几何参数配置
    const CONFIG = {
        speed: 180,           // X轴前进速度 (px/s)
        stairWidth: 700,      // 台阶宽度
        stairHeight: -160,    // 台阶高度 (负值向上)
        armLength: 90,        // 动画中的行星臂长 (代表真实世界的180mm,为了视觉比例调整)
        wheelRadius: 16,      // 小轮半径
        wheelBase: 360,       // 前后轮距
        chassisMountX: 160,   // 推杆在底盘的安装点X距离中心的绝对值
        platformMountX: 160,  // 推杆在平台的安装点X距离中心的绝对值
        defaultSuspensionH: 220, // 默认悬挂高度
        platformBaseY: -220   // 平台基础Y坐标(相对于世界参考系)
    };

    // DOM 元素缓存
    const els = {
        terrain: document.getElementById('terrain-group'),
        chassisGroup: document.getElementById('chassis-assembly'),
        frontWheelGroup: document.getElementById('front-wheel'),
        rearWheelGroup: document.getElementById('rear-wheel'),
        platformGroup: document.getElementById('payload-platform'),
        
        actFrontBody: document.getElementById('actuator-front-body'),
        actFrontRod: document.getElementById('actuator-front-rod'),
        jFrontChassis: document.getElementById('joint-front-chassis'),
        jFrontPlatform: document.getElementById('joint-front-platform'),
        
        actRearBody: document.getElementById('actuator-rear-body'),
        actRearRod: document.getElementById('actuator-rear-rod'),
        jRearChassis: document.getElementById('joint-rear-chassis'),
        jRearPlatform: document.getElementById('joint-rear-platform'),

        imuDataLine: document.getElementById('imu-data-line'),
        refLine: document.getElementById('horizontal-reference'),
        
        hudChassisAngle: document.getElementById('hud-chassis-angle'),
        hudFrontStroke: document.getElementById('hud-front-stroke'),
        hudRearStroke: document.getElementById('hud-rear-stroke'),
        hudResponseTime: document.getElementById('hud-response-time')
    };

    // 状态变量
    let globalTime = 0;
    let worldOffsetX = 0;
    let lastTimestamp = 0;
    
    // 生成无限延伸的台阶路径
    function generateTerrainSVG(startX, endX, offsetX) {
        let pathD = "";
        // 渲染视口内的台阶,前后多渲染一个保证无缝
        const startIdx = Math.floor((startX - offsetX) / CONFIG.stairWidth) - 1;
        const endIdx = Math.ceil((endX - offsetX) / CONFIG.stairWidth) + 1;

        for (let i = startIdx; i <= endIdx; i++) {
            const x0 = i * CONFIG.stairWidth + offsetX;
            const y0 = i * CONFIG.stairHeight;
            const x1 = (i + 1) * CONFIG.stairWidth + offsetX;
            const y1 = y0;
            
            if (i === startIdx) {
                pathD += `M ${x0} ${y0} L ${x1} ${y1} `;
            } else {
                // 画垂直面和水平面
                pathD += `L ${x0} ${y0} L ${x1} ${y1} `;
            }
        }
        
        // 闭合路径形成实体块
        pathD += `L ${(endIdx+1)*CONFIG.stairWidth + offsetX} 1000 L ${startIdx*CONFIG.stairWidth + offsetX} 1000 Z`;
        return `<path d="${pathD}" class="stair-path" />`;
    }

    // 核心缓动函数,模拟翻转物理过程
    function easeInOutCubic(x) {
        return x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2;
    }

    // 计算单个行星轮组在地形状况下的 Y轴高度 和 旋转角度
    function getWheelKinematics(absoluteX) {
        const localX = absoluteX % CONFIG.stairWidth;
        const stairIndex = Math.floor(absoluteX / CONFIG.stairWidth);
        let baseY = stairIndex * CONFIG.stairHeight;
        
        let extraY = 0;
        let rotation = 0;

        // 行星轮碰到台阶开始翻转的区域 (翻转区宽度略大于臂长,体现跨步动作)
        const flipZone = CONFIG.armLength * 1.8;
        const triggerX = CONFIG.stairWidth - flipZone;

        if (localX > triggerX) {
            // 在翻转区内
            let progress = (localX - triggerX) / flipZone;
            progress = Math.max(0, Math.min(1, progress));
            
            const eased = easeInOutCubic(progress);
            
            // Y轴高度变化 (从0变化到一个台阶高度)
            extraY = eased * CONFIG.stairHeight;
            
            // 行星架旋转 (90度翻转)
            rotation = eased * 90;
        }

        // 加上小轮半径导致的默认偏移
        return {
            y: baseY + extraY - CONFIG.wheelRadius,
            angle: rotation,
            isFlipping: localX > triggerX && localX < CONFIG.stairWidth
        };
    }

    // 更新逻辑
    function update(deltaTime) {
        // 世界向左滚动
        worldOffsetX -= (CONFIG.speed * deltaTime);

        // 保持机器人X坐标固定在屏幕上,相对地形X变化
        const robotAbsoluteX = -worldOffsetX;
        
        // 前后轮在地表上的绝对X坐标
        const frontAbsX = robotAbsoluteX + CONFIG.wheelBase / 2;
        const rearAbsX = robotAbsoluteX - CONFIG.wheelBase / 2;

        // 获取轮组姿态
        const frontState = getWheelKinematics(frontAbsX);
        const rearState = getWheelKinematics(rearAbsX);

        // 提取轮组高度差异
        const frontY = frontState.y;
        const rearY = rearState.y;

        // 计算底盘倾角和中心高度
        const dy = frontY - rearY;
        const dx = CONFIG.wheelBase;
        const chassisAngleRad = Math.atan2(dy, dx);
        const chassisAngleDeg = chassisAngleRad * (180 / Math.PI);
        const chassisCenterY = (frontY + rearY) / 2;

        // 视口相机跟踪底盘中心高度平滑移动,保证主体在画面中央
        // 这里简化处理,直接让平台固定在视图Y轴的一个绝对位置,底盘绕其下方的动态点运动
        const viewOffsetY = -chassisCenterY; 
        
        // --- 渲染更新 ---

        // 1. 更新地形
        els.terrain.innerHTML = generateTerrainSVG(-800, 800, worldOffsetX % (CONFIG.stairWidth * 10));
        els.terrain.setAttribute('transform', `translate(0, ${viewOffsetY})`);

        // 2. 更新底盘与车轮 (相对机器人中心)
        els.chassisGroup.setAttribute('transform', `translate(0, ${chassisCenterY + viewOffsetY}) rotate(${chassisAngleDeg})`);
        
        // 更新行星架内部旋转
        // 由于底盘已经倾斜,车轮自身需要加上相对于世界的滚动,并扣除底盘倾角保持视觉正确
        const rollingAngleFront = frontState.angle + (frontAbsX * 0.5); // 附加一点滚动效果
        const rollingAngleRear = rearState.angle + (rearAbsX * 0.5);
        
        els.frontWheelGroup.setAttribute('transform', `translate(${CONFIG.wheelBase/2}, 0) rotate(${frontState.angle})`);
        els.rearWheelGroup.setAttribute('transform', `translate(${-CONFIG.wheelBase/2}, 0) rotate(${rearState.angle})`);

        // 在进行翻转时给轮组添加高亮,强调"暴力克服"
        if (frontState.isFlipping) els.frontWheelGroup.style.filter = "url(#glow-orange)";
        else els.frontWheelGroup.style.filter = "none";
        
        if (rearState.isFlipping) els.rearWheelGroup.style.filter = "url(#glow-orange)";
        else els.rearWheelGroup.style.filter = "none";

        // 3. 计算主动推杆运动学 (核心IFR解耦展示)
        // 平台始终保持绝对水平,处于固定高度 (相对于屏幕)
        const platformY = CONFIG.platformBaseY; 
        els.platformGroup.setAttribute('transform', `translate(0, ${platformY})`);
        els.refLine.setAttribute('y1', platformY);
        els.refLine.setAttribute('y2', platformY);

        // 计算底盘上推杆安装点的世界坐标 (带相机偏移)
        const cosA = Math.cos(chassisAngleRad);
        const sinA = Math.sin(chassisAngleRad);
        
        // 前安装点 (底盘系) -> 世界系
        const fCx = CONFIG.chassisMountX * cosA;
        const fCy = chassisCenterY + viewOffsetY + (CONFIG.chassisMountX * sinA);
        
        // 后安装点 (底盘系) -> 世界系
        const rCx = -CONFIG.chassisMountX * cosA;
        const rCy = chassisCenterY + viewOffsetY + (-CONFIG.chassisMountX * sinA);

        // 平台上推杆安装点 (世界系,因平台水平所以X不变)
        const fPx = CONFIG.platformMountX;
        const fPy = platformY;
        
        const rPx = -CONFIG.platformMountX;
        const rPy = platformY;

        // 绘制前推杆
        updateActuator(els.jFrontChassis, els.jFrontPlatform, els.actFrontBody, els.actFrontRod, fCx, fCy, fPx, fPy);
        // 绘制后推杆
        updateActuator(els.jRearChassis, els.jRearPlatform, els.actRearBody, els.actRearRod, rCx, rCy, rPx, rPy);

        // 绘制IMU控制线 (从底盘中心连向平台)
        const imuX = -20 * cosA - (-27.5 * sinA); // 粗略估算IMU位置
        const imuY = chassisCenterY + viewOffsetY + (-20 * sinA) + (-27.5 * cosA);
        els.imuDataLine.setAttribute('d', `M ${imuX} ${imuY} Q 0 ${platformY + 50} 0 ${platformY}`);

        // 4. 更新HUD数据
        els.hudChassisAngle.textContent = (Math.abs(chassisAngleDeg) > 0.1 ? (chassisAngleDeg > 0 ? "+" : "") : "") + chassisAngleDeg.toFixed(2) + "°";
        els.hudChassisAngle.className = Math.abs(chassisAngleDeg) > 3 ? "hud-value val-orange" : "hud-value";

        const frontStroke = Math.sqrt(Math.pow(fPx-fCx, 2) + Math.pow(fPy-fCy, 2)) - Math.abs(CONFIG.platformBaseY);
        const rearStroke = Math.sqrt(Math.pow(rPx-rCx, 2) + Math.pow(rPy-rCy, 2)) - Math.abs(CONFIG.platformBaseY);
        
        els.hudFrontStroke.textContent = (frontStroke > 0 ? "+" : "") + frontStroke.toFixed(1) + " mm";
        els.hudRearStroke.textContent = (rearStroke > 0 ? "+" : "") + rearStroke.toFixed(1) + " mm";

        // 模拟高频响应时间抖动 (体现<20ms的要求)
        if (Math.random() > 0.8) {
            els.hudResponseTime.textContent = (12 + Math.random() * 6).toFixed(1) + " ms";
        }
    }

    // 更新单个推杆的SVG元素
    function updateActuator(jointC, jointP, body, rod, cx, cy, px, py) {
        // 更新关节点
        jointC.setAttribute('cx', cx); jointC.setAttribute('cy', cy);
        jointP.setAttribute('cx', px); jointP.setAttribute('cy', py);
        
        // 计算连线角度和分段
        // 推杆缸体连接底盘,活塞杆连接平台 (或者相反,这里采用缸体在下)
        const totalLen = Math.sqrt(Math.pow(px-cx, 2) + Math.pow(py-cy, 2));
        const bodyLen = 80; // 缸体固定长度视效
        
        const dirX = (px - cx) / totalLen;
        const dirY = (py - cy) / totalLen;
        
        const midX = cx + dirX * bodyLen;
        const midY = cy + dirY * bodyLen;

        // 绘制缸体
        body.setAttribute('x1', cx); body.setAttribute('y1', cy);
        body.setAttribute('x2', midX); body.setAttribute('y2', midY);
        
        // 绘制活塞杆
        rod.setAttribute('x1', midX); rod.setAttribute('y1', midY);
        rod.setAttribute('x2', px); rod.setAttribute('y2', py);
    }

    // 主循环
    function render(timestamp) {
        if (!lastTimestamp) lastTimestamp = timestamp;
        // 限制最大deltaTime,防止切换标签页后时间跳跃导致错乱
        const deltaTime = Math.min((timestamp - lastTimestamp) / 1000, 0.05); 
        lastTimestamp = timestamp;

        update(deltaTime);

        requestAnimationFrame(render);
    }

    // 初始化启动 (满足加载后自动播放的要求)
    document.addEventListener("DOMContentLoaded", () => {
        requestAnimationFrame(render);
    });

</script>
</body>
</html>
积分规则:第一轮对话扣减8分,后续每轮扣6分