分享图
动画工坊
引擎就绪
<!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-color: #060913;
            --panel-bg: rgba(13, 20, 36, 0.7);
            --border-color: rgba(0, 240, 255, 0.2);
            --text-main: #e2e8f0;
            --text-muted: #64748b;
            --energy-color: #00f0ff;
            --kinetic-color: #ff5500;
            --signal-color: #10b981;
            
            /* Dynamic variables controlled by JS */
            --flow-speed: 2s;
            --energy-opacity: 0.5;
            --charge-level: 50%;
            --charge-color: #10b981;
        }

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

        body {
            background-color: var(--bg-color);
            color: var(--text-main);
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            overflow: hidden;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background-image: 
                radial-gradient(circle at 50% 50%, #0d1627 0%, #03050a 100%),
                linear-gradient(rgba(0, 240, 255, 0.03) 1px, transparent 1px),
                linear-gradient(90deg, rgba(0, 240, 255, 0.03) 1px, transparent 1px);
            background-size: 100% 100%, 40px 40px, 40px 40px;
        }

        /* 绝对定位的 UI 层,严格控制尺寸和位置,不遮挡核心动画 */
        .overlay-panel {
            position: absolute;
            background: var(--panel-bg);
            backdrop-filter: blur(10px);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            padding: 12px 16px;
            z-index: 10;
        }

        .top-left {
            top: 24px;
            left: 24px;
        }

        .top-right {
            top: 24px;
            right: 24px;
            text-align: right;
        }

        .bottom-center {
            bottom: 32px;
            left: 50%;
            transform: translateX(-50%);
            display: flex;
            align-items: center;
            gap: 16px;
            padding: 16px 24px;
        }

        h1 {
            font-size: 14px;
            font-weight: 600;
            letter-spacing: 1px;
            color: var(--energy-color);
            margin-bottom: 4px;
        }

        p, .param-text {
            font-size: 12px;
            color: var(--text-muted);
            line-height: 1.4;
        }

        .param-value {
            color: var(--text-main);
            font-family: monospace;
            font-size: 13px;
        }

        /* 交互滑块样式 */
        .slider-container {
            display: flex;
            flex-direction: column;
            gap: 6px;
            min-width: 200px;
        }
        
        .slider-header {
            display: flex;
            justify-content: space-between;
            font-size: 12px;
            color: var(--text-muted);
        }

        input[type=range] {
            -webkit-appearance: none;
            width: 100%;
            background: transparent;
        }

        input[type=range]:focus {
            outline: none;
        }

        input[type=range]::-webkit-slider-runnable-track {
            width: 100%;
            height: 4px;
            cursor: pointer;
            background: rgba(255, 255, 255, 0.1);
            border-radius: 2px;
        }

        input[type=range]::-webkit-slider-thumb {
            height: 16px;
            width: 16px;
            border-radius: 50%;
            background: var(--kinetic-color);
            cursor: pointer;
            -webkit-appearance: none;
            margin-top: -6px;
            box-shadow: 0 0 10px var(--kinetic-color);
            transition: transform 0.1s;
        }

        input[type=range]::-webkit-slider-thumb:hover {
            transform: scale(1.2);
        }

        /* 核心动画容器 */
        .svg-container {
            width: 100%;
            max-width: 1200px;
            height: 100%;
            max-height: 800px;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 40px;
        }

        svg {
            width: 100%;
            height: 100%;
            overflow: visible;
        }

        /* CSS 动画类 */
        .flow-line {
            stroke-dasharray: 8 8;
            animation: flow var(--flow-speed) linear infinite;
            opacity: var(--energy-opacity);
            transition: opacity 0.3s;
        }

        @keyframes flow {
            to { stroke-dashoffset: -16; }
        }

        .signal-arc {
            opacity: 0;
            transform-origin: 820px 320px;
        }

        @keyframes radiate {
            0% { transform: scale(0.8); opacity: 0; }
            50% { opacity: var(--energy-opacity); }
            100% { transform: scale(1.8); opacity: 0; }
        }
    </style>
</head>
<body>

    <!-- 顶部左侧:标题与机理说明 -->
    <div class="overlay-panel top-left">
        <h1>无源物联网:压电振动能量采集系统</h1>
        <p>通过悬臂梁放大筛箱振动,压电陶瓷形变产生电能<br>彻底消除外部供电依赖 (IFR)</p>
    </div>

    <!-- 顶部右侧:关键参数与状态 -->
    <div class="overlay-panel top-right">
        <p>设计功率输出: <span class="param-value">0.5~2W</span></p>
        <p>超级电容储能: <span class="param-value">100F/2.7V</span></p>
        <div style="margin-top: 8px; border-top: 1px solid var(--border-color); padding-top: 8px;">
            <p>实时采集效率: <span class="param-value" id="val-efficiency" style="color: var(--energy-color);">0%</span></p>
            <p>储能状态: <span class="param-value" id="val-storage" style="color: var(--charge-color);">0%</span></p>
        </div>
    </div>

    <!-- 底部控制:交互引导 -->
    <div class="overlay-panel bottom-center">
        <div class="slider-container">
            <div class="slider-header">
                <span>环境振动幅值控制</span>
                <span id="val-vibration" style="color: var(--kinetic-color); font-family: monospace;">极低</span>
            </div>
            <input type="range" id="vibration-slider" min="0" max="100" value="50">
        </div>
        <p style="max-width: 200px; font-size: 11px; margin-left: 12px; border-left: 1px solid rgba(255,255,255,0.1); padding-left: 12px;">
            调节环境振动幅度<br>观察压电能量转换及储能缓冲机制
        </p>
    </div>

    <!-- 核心可视化区域 -->
    <div class="svg-container">
        <svg viewBox="0 0 1000 600" preserveAspectRatio="xMidYMid meet">
            <defs>
                <!-- 发光滤镜 -->
                <filter id="glow-cyan" x="-20%" y="-20%" width="140%" height="140%">
                    <feGaussianBlur stdDeviation="4" result="blur" />
                    <feComposite in="SourceGraphic" in2="blur" operator="over" />
                </filter>
                <filter id="glow-orange" x="-20%" y="-20%" width="140%" height="140%">
                    <feGaussianBlur stdDeviation="6" result="blur" />
                    <feComposite in="SourceGraphic" in2="blur" operator="over" />
                </filter>
                
                <!-- 渐变定义 -->
                <linearGradient id="metal-grad" x1="0%" y1="0%" x2="0%" y2="100%">
                    <stop offset="0%" stop-color="#334155" />
                    <stop offset="50%" stop-color="#475569" />
                    <stop offset="100%" stop-color="#1e293b" />
                </linearGradient>
                
                <linearGradient id="piezo-grad" x1="0%" y1="0%" x2="100%" y2="0%">
                    <stop offset="0%" stop-color="#0284c7" />
                    <stop offset="100%" stop-color="#00f0ff" />
                </linearGradient>

                <linearGradient id="module-bg" x1="0%" y1="0%" x2="100%" y2="100%">
                    <stop offset="0%" stop-color="rgba(30, 41, 59, 0.8)" />
                    <stop offset="100%" stop-color="rgba(15, 23, 42, 0.8)" />
                </linearGradient>
            </defs>

            <!-- 背景装饰线 -->
            <path d="M 150 450 L 850 450" stroke="rgba(255,255,255,0.05)" stroke-width="2" stroke-dasharray="10 5" />
            
            <!-- 动态接线 (由JS更新路径) -->
            <path id="wire-dynamic" d="M 280 300 C 350 300, 400 240, 500 240" fill="none" stroke="#00f0ff" stroke-width="2" class="flow-line" filter="url(#glow-cyan)" />
            <path d="M 620 240 L 740 240" fill="none" stroke="#00f0ff" stroke-width="2" class="flow-line" filter="url(#glow-cyan)" />
            <path d="M 800 290 L 800 370" fill="none" stroke="#00f0ff" stroke-width="2" class="flow-line" filter="url(#glow-cyan)" />

            <!-- === 物理振动系统 === -->
            <g id="vibration-system">
                <!-- 振动源基座 (筛箱) -->
                <rect x="80" y="220" width="100" height="160" rx="8" fill="url(#metal-grad)" stroke="#64748b" stroke-width="2" />
                <circle cx="130" cy="300" r="12" fill="#0f172a" stroke="#475569" stroke-width="3" />
                
                <text x="130" y="405" fill="var(--text-muted)" font-size="12" text-anchor="middle">振动体(筛箱)</text>
                
                <!-- 能量波纹(表示动能) -->
                <g id="kinetic-waves" filter="url(#glow-orange)" opacity="0.5">
                    <path d="M 50 250 Q 30 300 50 350" fill="none" stroke="var(--kinetic-color)" stroke-width="2" />
                    <path d="M 30 230 Q 0 300 30 370" fill="none" stroke="var(--kinetic-color)" stroke-width="2" />
                </g>

                <!-- 悬臂梁与压电组件 (绕支点旋转) -->
                <g id="cantilever-assembly">
                    <!-- 主梁 -->
                    <rect x="130" y="292" width="220" height="16" fill="#94a3b8" />
                    
                    <!-- 压电陶瓷片 (上下各一) -->
                    <rect id="piezo-top" x="160" y="284" width="100" height="8" rx="2" fill="url(#piezo-grad)" filter="url(#glow-cyan)" />
                    <rect id="piezo-bottom" x="160" y="308" width="100" height="8" rx="2" fill="url(#piezo-grad)" filter="url(#glow-cyan)" />
                    
                    <text x="210" y="275" fill="#00f0ff" font-size="11" text-anchor="middle" filter="url(#glow-cyan)">压电能量采集器</text>

                    <!-- 质量块 -->
                    <circle cx="350" cy="300" r="24" fill="url(#metal-grad)" stroke="#cbd5e1" stroke-width="2" />
                    <circle cx="350" cy="300" r="8" fill="#475569" />
                    <text x="350" y="340" fill="var(--text-muted)" font-size="11" text-anchor="middle">质量块放大</text>
                </g>
            </g>


            <!-- === 固定电路模块 === -->
            <!-- 1. 整流/DCDC 管理模块 -->
            <g transform="translate(480, 190)">
                <rect x="0" y="0" width="140" height="100" rx="12" fill="url(#module-bg)" stroke="var(--border-color)" stroke-width="1.5" />
                <path d="M 20 50 L 40 30 L 70 70 L 90 30 L 120 50" fill="none" stroke="#64748b" stroke-width="2" stroke-linejoin="round" opacity="0.5"/>
                <!-- 动态整流指示 -->
                <path d="M 20 50 L 40 30 L 70 70 L 90 30 L 120 50" fill="none" stroke="#00f0ff" stroke-width="2" stroke-linejoin="round" class="flow-line" filter="url(#glow-cyan)" />
                <text x="70" y="125" fill="var(--text-muted)" font-size="12" text-anchor="middle">整流与DCDC稳压</text>
            </g>

            <!-- 2. 超级电容储能模块 -->
            <g transform="translate(740, 150)">
                <rect x="0" y="0" width="120" height="140" rx="12" fill="url(#module-bg)" stroke="var(--border-color)" stroke-width="1.5" />
                <!-- 电池外壳 -->
                <rect x="35" y="20" width="50" height="90" rx="4" fill="none" stroke="#475569" stroke-width="2" />
                <rect x="50" y="12" width="20" height="8" rx="2" fill="#475569" />
                
                <!-- 动态电量填充 -->
                <rect id="charge-fill" x="37" y="108" width="46" height="0" rx="2" fill="var(--charge-color)" style="transition: height 0.3s, y 0.3s, fill 0.5s;" />
                
                <!-- 电量网格线 -->
                <line x1="35" y1="42.5" x2="85" y2="42.5" stroke="#1e293b" stroke-width="2" />
                <line x1="35" y1="65" x2="85" y2="65" stroke="#1e293b" stroke-width="2" />
                <line x1="35" y1="87.5" x2="85" y2="87.5" stroke="#1e293b" stroke-width="2" />
                
                <text x="60" y="135" fill="var(--text-main)" font-size="11" text-anchor="middle" font-family="monospace">100F/2.7V</text>
                <text x="60" y="165" fill="var(--text-muted)" font-size="12" text-anchor="middle">超级电容缓冲</text>
            </g>

            <!-- 3. 无线传感节点 (LoRa/WiFi) -->
            <g transform="translate(720, 370)">
                <rect x="0" y="0" width="160" height="80" rx="12" fill="url(#module-bg)" stroke="var(--border-color)" stroke-width="1.5" />
                <circle cx="40" cy="40" r="15" fill="#0f172a" stroke="#94a3b8" stroke-width="2" />
                <circle id="sensor-indicator" cx="40" cy="40" r="6" fill="#10b981" />
                
                <!-- 天线 -->
                <path d="M 100 20 L 100 -20" fill="none" stroke="#94a3b8" stroke-width="3" stroke-linecap="round" />
                <circle cx="100" cy="-20" r="4" fill="#10b981" />
                
                <text x="40" y="70" fill="var(--text-muted)" font-size="10" text-anchor="middle">液位/应变</text>
                <text x="100" y="60" fill="var(--text-main)" font-size="13" text-anchor="middle">无线传输节点</text>

                <!-- 无线信号波纹 -->
                <g id="signal-waves" stroke="#10b981" fill="none" stroke-width="2" stroke-linecap="round">
                    <path class="signal-arc" d="M 120 -40 Q 140 -20 120 0" style="animation: radiate 2s infinite 0s;" />
                    <path class="signal-arc" d="M 135 -55 Q 165 -20 135 15" style="animation: radiate 2s infinite 0.66s;" />
                    <path class="signal-arc" d="M 150 -70 Q 190 -20 150 30" style="animation: radiate 2s infinite 1.33s;" />
                </g>
            </g>
            
            <!-- 连接指示点 -->
            <circle cx="500" cy="240" r="4" fill="#00f0ff" filter="url(#glow-cyan)" />
            <circle cx="620" cy="240" r="4" fill="#00f0ff" filter="url(#glow-cyan)" />
            <circle cx="740" cy="240" r="4" fill="#00f0ff" filter="url(#glow-cyan)" />
            <circle cx="800" cy="290" r="4" fill="#00f0ff" filter="url(#glow-cyan)" />
            <circle cx="800" cy="370" r="4" fill="#00f0ff" filter="url(#glow-cyan)" />

        </svg>
    </div>

    <script>
        document.addEventListener("DOMContentLoaded", () => {
            const root = document.documentElement;
            const slider = document.getElementById('vibration-slider');
            
            // DOM 元素引用
            const baseElement = document.getElementById('vibration-system');
            const cantilever = document.getElementById('cantilever-assembly');
            const dynamicWire = document.getElementById('wire-dynamic');
            const piezoTop = document.getElementById('piezo-top');
            const piezoBottom = document.getElementById('piezo-bottom');
            const chargeFill = document.getElementById('charge-fill');
            const sensorInd = document.getElementById('sensor-indicator');
            const signalArcs = document.querySelectorAll('.signal-arc');
            
            const valVib = document.getElementById('val-vibration');
            const valEff = document.getElementById('val-efficiency');
            const valStore = document.getElementById('val-storage');
            const kineticWaves = document.getElementById('kinetic-waves');

            // 物理仿真状态
            let time = 0;
            let currentAmp = 0.5;  // 当前振幅 (0-1)
            let targetAmp = 0.5;   // 目标振幅 (0-1)
            let chargeLevel = 20;  // 当前电量 (0-100)

            // 动画主循环
            function animate() {
                // 1. 平滑插值振幅 (用户交互响应)
                targetAmp = slider.value / 100;
                currentAmp += (targetAmp - currentAmp) * 0.05;

                // 更新界面文本
                if (targetAmp < 0.2) valVib.textContent = '微弱';
                else if (targetAmp < 0.6) valVib.textContent = '中等';
                else valVib.textContent = '剧烈';

                // 2. 物理运动计算
                // 基座垂直简谐振动
                const baseMaxY = 20; 
                const baseY = Math.sin(time) * currentAmp * baseMaxY;
                
                // 悬臂梁弯曲 (因质量块惯性产生相位滞后和角度变形)
                const beamMaxAngle = 12; 
                const beamAngle = Math.sin(time - Math.PI / 3) * currentAmp * beamMaxAngle;

                // 应用变换
                baseElement.setAttribute('transform', `translate(0, ${baseY})`);
                // 绕左侧支撑点 (x=130, y=300) 旋转
                cantilever.setAttribute('transform', `rotate(${beamAngle}, 130, 300)`);

                // 3. 动态接线更新 (起点随基座运动,终点固定在整流器)
                // 起点位于压电片引出端:近似计算其随基座和梁的绝对坐标,简化起见绑定在基座上即可
                const wireStartX = 280;
                const wireStartY = 300 + baseY;
                dynamicWire.setAttribute('d', `M ${wireStartX} ${wireStartY} C 350 ${wireStartY}, 400 240, 500 240`);

                // 4. 发电视觉反馈
                // 压电片亮度随形变(角度)变化
                const stress = Math.abs(beamAngle) / beamMaxAngle;
                const activeOpacity = 0.3 + (stress * 0.7);
                piezoTop.style.opacity = activeOpacity;
                piezoBottom.style.opacity = activeOpacity;
                kineticWaves.style.opacity = currentAmp * 0.8;

                // 更新 CSS 变量驱动能量流
                const energyFlow = Math.max(0, (currentAmp - 0.1) * 1.1); // 存在死区,振幅极小不发电
                root.style.setProperty('--energy-opacity', energyFlow.toFixed(2));
                root.style.setProperty('--flow-speed', (3 - currentAmp * 2) + 's');
                
                valEff.textContent = (energyFlow * 100).toFixed(0) + '%';

                // 5. 储能系统逻辑 (充放电平衡)
                // 发电量与消耗量抗衡
                const generateRate = energyFlow * 0.3;
                const consumeRate = chargeLevel > 10 ? 0.1 : 0; // 如果有电则消耗
                
                chargeLevel += (generateRate - consumeRate);
                chargeLevel = Math.max(0, Math.min(100, chargeLevel)); // 限制在 0-100

                // 更新电量 UI
                const height = (chargeLevel / 100) * 88;
                const yPos = 108 - height;
                chargeFill.setAttribute('height', height);
                chargeFill.setAttribute('y', yPos);
                valStore.textContent = chargeLevel.toFixed(1) + '%';

                // 电量颜色映射 (低电量红,正常绿)
                let capColor = '#10b981'; // 绿
                if (chargeLevel < 20) capColor = '#ef4444'; // 红
                else if (chargeLevel < 50) capColor = '#f59e0b'; // 黄
                
                root.style.setProperty('--charge-color', capColor);
                sensorInd.setAttribute('fill', chargeLevel > 10 ? capColor : '#475569');

                // 6. 无线传输控制
                if (chargeLevel > 10) {
                    signalArcs.forEach(arc => {
                        arc.style.animationPlayState = 'running';
                        arc.style.stroke = capColor;
                    });
                } else {
                    signalArcs.forEach(arc => arc.style.animationPlayState = 'paused');
                }

                // 步进时间 (频率随振幅微调,更真实)
                time += 0.15 + (currentAmp * 0.1);
                requestAnimationFrame(animate);
            }

            // 启动动画循环
            animate();
        });
    </script>
</body>
</html>
积分规则:每轮对话扣10分