html制作太阳系行星运行轨道演示动画

使用 html 和 js 制作太阳系八大行星运行轨道演示动画

代码如下:

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>
        body { margin: 0; background: #020205; color: white; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; overflow: hidden; }
        canvas { display: block; cursor: move; }
        
        /* 通用面板样式 */
        .panel {
            position: absolute;
            background: rgba(10, 10, 20, 0.85); 
            border: 1px solid rgba(255, 255, 255, 0.1);
            backdrop-filter: blur(8px);
            transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
            z-index: 10;
        }

        /* 左侧面板 */
        #ui-panel { 
            top: 20px; left: 20px; 
            padding: 20px; border-radius: 12px; 
            max-width: 320px;
        }
        #ui-panel.collapsed { transform: translateX(-110%); }

        /* 右侧面板 */
        #controls { 
            bottom: 20px; right: 20px; 
            padding: 20px; border-radius: 12px;
            display: flex; flex-direction: column; gap: 15px;
        }
        #controls.collapsed { transform: translateX(110%); }

        /* 折叠按钮样式 */
        .toggle-btn {
            position: absolute;
            width: 30px; height: 30px;
            background: rgba(40, 40, 60, 0.9);
            border: 1px solid rgba(255, 255, 255, 0.2);
            border-radius: 50%;
            color: #ffcc00;
            cursor: pointer;
            display: flex; align-items: center; justify-content: center;
            font-size: 16px; transition: 0.3s;
            pointer-events: auto;
        }
        .toggle-btn:hover { background: #ffcc00; color: #000; }

        #btn-left { right: -40px; top: 0; }
        #btn-right { left: -40px; bottom: 0; }

        h1 { margin: 0 0 10px 0; font-size: 20px; color: #ffcc00; }
        p { font-size: 13px; margin: 5px 0; opacity: 0.8; }
        table { width: 100%; border-collapse: collapse; margin-top: 15px; font-size: 12px; }
        th { text-align: left; border-bottom: 1px solid #444; padding-bottom: 5px; }
        td { padding: 4px 0; }
        .val { color: #ffcc00; font-weight: bold; }

        .ctrl-item { display: flex; align-items: center; justify-content: space-between; gap: 15px; font-size: 13px; }
        input[type=range] { width: 120px; cursor: pointer; accent-color: #ffcc00; }
        .check-group { font-size: 14px; cursor: pointer; display: flex; align-items: center; gap: 8px; font-weight: bold; color: #ffcc00; }
    </style>
</head>
<body>

    <div id="ui-panel" class="panel">
        <div id="btn-left" class="toggle-btn" onclick="togglePanel('ui-panel')">◀</div>
        <h1>太阳系探索器</h1>
        <p>🖱️ <b>滚动</b> 以缩放视野</p>
        <p>🖱️ <b>左键拖拽</b> 以移动位置</p>
        <div id="planet-info"></div>
    </div>

    <div id="controls" class="panel">
        <div id="btn-right" class="toggle-btn" onclick="togglePanel('controls')">▶</div>
        <label class="check-group">
            <input type="checkbox" id="orbit-toggle" checked> 显示运行轨道
        </label>
        
        <div class="ctrl-item">
            轨道亮度: <input type="range" id="orbit-opacity" min="0" max="1" step="0.05" value="0.3">
        </div>
        
        <div class="ctrl-item">
            轨道粗细: <input type="range" id="orbit-width" min="0.5" max="5" step="0.5" value="1">
        </div>

        <div class="ctrl-item">
            运行速度: <input type="range" id="speed-slider" min="0" max="10" step="0.5" value="1">
        </div>
    </div>

    <canvas id="solarCanvas"></canvas>

    <script>
        const canvas = document.getElementById('solarCanvas');
        const ctx = canvas.getContext('2d');

        let width, height, scale = 0.8, offsetX = 0, offsetY = 0;
        let speedMult = 1, showOrbits = true, orbitOpacity = 0.3, orbitStrokeWidth = 1;
        let isDragging = false, lastMouseX, lastMouseY;

        const planets = [
            { name: "水星", color: "#A5A5A5", dist: 65, radius: 4, rev: 88, rot: 1407, angle: Math.random()*7 },
            { name: "金星", color: "#E3BB76", dist: 100, radius: 7, rev: 224.7, rot: -5832, angle: Math.random()*7 },
            { name: "地球", color: "#2271B3", dist: 145, radius: 7.5, rev: 365.2, rot: 23.9, angle: Math.random()*7 },
            { name: "火星", color: "#E27B58", dist: 190, radius: 5, rev: 687, rot: 24.6, angle: Math.random()*7 },
            { name: "木星", color: "#D39C7E", dist: 270, radius: 18, rev: 4332, rot: 9.9, angle: Math.random()*7 },
            { name: "土星", color: "#C5AB6E", dist: 350, radius: 15, rev: 10759, rot: 10.7, ring: true, angle: Math.random()*7 },
            { name: "天王星", color: "#BBE1E4", dist: 420, radius: 10, rev: 30687, rot: -17.2, angle: Math.random()*7 },
            { name: "海王星", color: "#6081FF", dist: 480, radius: 10, rev: 60190, rot: 16.1, angle: Math.random()*7 }
        ];

        const stars = Array.from({ length: 400 }, () => ({
            x: Math.random() * 4000 - 2000,
            y: Math.random() * 4000 - 2000,
            size: Math.random() * 1.5
        }));

        // 切换面板显示状态
        function togglePanel(id) {
            const el = document.getElementById(id);
            const btn = el.querySelector('.toggle-btn');
            el.classList.toggle('collapsed');
            
            // 更新箭头方向
            if(id === 'ui-panel') {
                btn.innerText = el.classList.contains('collapsed') ? '▶' : '◀';
            } else {
                btn.innerText = el.classList.contains('collapsed') ? '◀' : '▶';
            }
        }

        function init() {
            resize();
            createTable();
            window.addEventListener('resize', resize);
            window.addEventListener('wheel', e => {
                e.preventDefault();
                scale = Math.min(Math.max(scale - e.deltaY * 0.001, 0.05), 5);
            }, { passive: false });
            
            canvas.addEventListener('mousedown', e => { isDragging = true; lastMouseX = e.clientX; lastMouseY = e.clientY; });
            window.addEventListener('mousemove', e => {
                if (isDragging) {
                    offsetX += e.clientX - lastMouseX;
                    offsetY += e.clientY - lastMouseY;
                    lastMouseX = e.clientX; lastMouseY = e.clientY;
                }
            });
            window.addEventListener('mouseup', () => isDragging = false);
            
            document.getElementById('speed-slider').oninput = e => speedMult = e.target.value;
            document.getElementById('orbit-toggle').onchange = e => showOrbits = e.target.checked;
            document.getElementById('orbit-opacity').oninput = e => orbitOpacity = e.target.value;
            document.getElementById('orbit-width').oninput = e => orbitStrokeWidth = e.target.value;

            animate();
        }

        function resize() {
            width = window.innerWidth; height = window.innerHeight;
            canvas.width = width; canvas.height = height;
        }

        function draw() {
            ctx.fillStyle = "#020205";
            ctx.fillRect(0, 0, width, height);
            ctx.save();
            ctx.translate(width / 2 + offsetX, height / 2 + offsetY);
            ctx.scale(scale, scale);

            stars.forEach(s => {
                ctx.globalAlpha = 0.5; ctx.fillStyle = "white";
                ctx.beginPath(); ctx.arc(s.x, s.y, s.size, 0, Math.PI * 2); ctx.fill();
            });
            ctx.globalAlpha = 1.0;

            const sunGradient = ctx.createRadialGradient(0, 0, 0, 0, 0, 40);
            sunGradient.addColorStop(0, "#FFF5F2"); sunGradient.addColorStop(0.2, "#FFCC00");
            sunGradient.addColorStop(1, "transparent");
            ctx.beginPath(); ctx.arc(0, 0, 50, 0, Math.PI * 2); ctx.fillStyle = sunGradient; ctx.fill();

            planets.forEach(p => {
                if (showOrbits) {
                    ctx.beginPath();
                    ctx.arc(0, 0, p.dist, 0, Math.PI * 2);
                    ctx.strokeStyle = `rgba(255, 255, 255, ${orbitOpacity})`;
                    ctx.lineWidth = orbitStrokeWidth / scale;
                    ctx.stroke();
                }

                p.angle += (0.01 * (365 / p.rev)) * speedMult;
                const x = Math.cos(p.angle) * p.dist;
                const y = Math.sin(p.angle) * p.dist;

                if (p.ring) {
                    ctx.beginPath(); ctx.ellipse(x, y, p.radius * 2.2, p.radius * 0.8, p.angle, 0, Math.PI * 2);
                    ctx.strokeStyle = "rgba(197, 171, 110, 0.5)"; ctx.lineWidth = 2/scale; ctx.stroke();
                }

                ctx.beginPath(); ctx.arc(x, y, p.radius, 0, Math.PI * 2);
                ctx.fillStyle = p.color; ctx.shadowBlur = 15; ctx.shadowColor = p.color; ctx.fill();
                ctx.shadowBlur = 0;
                ctx.fillStyle = "rgba(255, 255, 255, 0.8)"; ctx.font = `${12 / scale}px sans-serif`;
                ctx.fillText(p.name, x + p.radius + 5, y + 5);
            });
            ctx.restore();
        }

        function createTable() {
            const infoDiv = document.getElementById('planet-info');
            let html = `<table><tr><th>行星</th><th>公转</th><th>自转</th></tr>`;
            planets.forEach(p => {
                html += `<tr><td>${p.name}</td><td class="val">${p.rev}d</td><td class="val">${Math.abs(p.rot)}h</td></tr>`;
            });
            infoDiv.innerHTML = html + `</table>`;
        }

        function animate() { draw(); requestAnimationFrame(animate); }
        init();
    </script>
</body>
</html>

将该文件保存为SolarSystem.html ,然后浏览器打开

效果:

相关推荐
C_心欲无痕1 天前
网络相关 - http1.1 与 http2
前端·网络
一只爱吃糖的小羊1 天前
Web Worker 性能优化实战:将计算密集型逻辑从主线程剥离的正确姿势
前端·性能优化
低保和光头哪个先来1 天前
源码篇 实例方法
前端·javascript·vue.js
你真的可爱呀1 天前
自定义颜色选择功能
开发语言·前端·javascript
小王和八蛋1 天前
JS中 escape urlencodeComponent urlencode 区别
前端·javascript
奔跑的web.1 天前
TypeScript类型系统核心速通:从基础到常用复合类型包装类
开发语言·前端·javascript·typescript·vue
Misnice1 天前
Webpack、Vite 、Rsbuild 区别
前端·webpack·node.js
Kagol1 天前
🎉历时1年,TinyEditor v4.0 正式发布!
前端·typescript·开源
丶一派胡言丶1 天前
02-VUE介绍和指令
前端·javascript·vue.js