docs: case04 Readme.html 新增交互式轨道示意图

使用 HTML5 Canvas + JavaScript 绘制椭圆轨道图,
支持滑块调整偏心率 e(0~0.85),实时更新:
- 椭圆形状变化
- a/b/c 参数标注
- 近日点/远日点数值
- 速度矢量方向
- 底部信息面板
This commit is contained in:
2026-06-18 23:01:04 +08:00
parent f6c468702f
commit 84aed813df
+302 -4
View File
@@ -116,8 +116,69 @@ MathJax = {
padding: 8px 0;
}
.diagram-wrap {
background: #0d1117;
border: 1px solid var(--border);
border-radius: 8px;
padding: 16px;
margin: 16px 0;
text-align: center;
}
.diagram-wrap canvas {
display: block;
margin: 0 auto;
max-width: 100%;
height: auto;
}
.diagram-controls {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: 16px;
margin-top: 12px;
}
.diagram-controls label {
font-size: 0.85rem;
color: var(--text-dim);
}
.diagram-controls input[type="range"] {
width: 200px;
accent-color: var(--accent);
}
.diagram-controls .val {
font-size: 0.9rem;
font-weight: 500;
color: var(--teal);
min-width: 48px;
display: inline-block;
text-align: center;
}
.diagram-info {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 20px;
margin-top: 10px;
font-size: 0.82rem;
color: var(--text-dim);
}
.diagram-info span {
display: inline-flex;
align-items: center;
gap: 4px;
}
.diagram-info .dot {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: 2px;
}
@media (max-width: 640px) {
body { padding: 16px; }
.diagram-controls { flex-direction: column; gap: 8px; }
}
</style>
</head>
@@ -175,7 +236,20 @@ MathJax = {
<h2>三、轨道力学</h2>
<h3>3.1 偏心率定义</h3>
<h3>3.1 交互式轨道示意图</h3>
<p>拖动下方滑块改变偏心率 $e$,观察轨道形状的变化和参数标注:</p>
<div class="diagram-wrap">
<canvas id="orbitCanvas" width="760" height="400"></canvas>
<div class="diagram-controls">
<label>偏心率 e = <span class="val" id="eDisplay">0.50</span></label>
<input type="range" id="eSlider" min="0" max="85" value="50">
</div>
<div class="diagram-info" id="diagramInfo"></div>
</div>
<h3>3.2 偏心率定义</h3>
<div class="highlight-eq">
<p>偏心率 $e$ 描述椭圆轨道偏离正圆的程度:</p>
@@ -193,7 +267,7 @@ MathJax = {
<tr><td>$e &gt; 1$</td><td>双曲线</td><td>飞越轨道,速度超过逃逸速度</td></tr>
</table>
<h3>3.2 半长轴与半短轴的关系</h3>
<h3>3.3 半长轴与半短轴的关系</h3>
<div class="highlight-eq">
<div class="formula-block">
@@ -202,7 +276,7 @@ MathJax = {
<p>当 $e \ll 1$ 时,$b \approx a\left(1 - \dfrac{e^2}{2}\right)$,椭圆度非常微小。</p>
</div>
<h3>3.3 近日点与远日点</h3>
<h3>3.4 近日点与远日点</h3>
<div class="highlight-eq">
<p>近日点(距太阳最近)和远日点(距太阳最远)的距离分别为:</p>
@@ -216,7 +290,7 @@ MathJax = {
</div>
</div>
<h3>3.4 开普勒三大定律</h3>
<h3>3.5 开普勒三大定律</h3>
<ol>
<li><strong>椭圆定律</strong>:行星轨道是椭圆,太阳位于椭圆的一个焦点上。</li>
@@ -290,5 +364,229 @@ MathJax = {
</ul>
</div>
<script>
(function() {
var canvas = document.getElementById('orbitCanvas');
var ctx = canvas.getContext('2d');
var slider = document.getElementById('eSlider');
var eDisplay = document.getElementById('eDisplay');
var infoDiv = document.getElementById('diagramInfo');
var W = 760, H = 400;
var cx = 380, cy = 200;
// 实际绘图区域半径
var A = 140;
function draw(e) {
ctx.clearRect(0, 0, W, H);
var b = A * Math.sqrt(1 - e * e);
var f = e * A;
// 长轴辅助线
ctx.beginPath();
ctx.moveTo(cx - A - 20, cy);
ctx.lineTo(cx + A + 20, cy);
ctx.strokeStyle = '#30363d';
ctx.lineWidth = 0.5;
ctx.setLineDash([4, 3]);
ctx.stroke();
ctx.setLineDash([]);
// 椭圆轨道
ctx.beginPath();
ctx.ellipse(cx - f, cy, A, b, 0, 0, Math.PI * 2);
ctx.strokeStyle = '#58a6ff';
ctx.lineWidth = 1.5;
ctx.stroke();
// 半长轴 a(标注线)
var aY = cy + 28;
ctx.beginPath();
ctx.moveTo(cx - f, aY);
ctx.lineTo(cx - f + A, aY);
ctx.strokeStyle = '#c9d1d9';
ctx.lineWidth = 1.5;
ctx.stroke();
// 箭头
ctx.beginPath();
ctx.moveTo(cx - f + 6, aY - 4);
ctx.lineTo(cx - f, aY);
ctx.lineTo(cx - f + 6, aY + 4);
ctx.strokeStyle = '#c9d1d9';
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(cx - f + A - 6, aY - 4);
ctx.lineTo(cx - f + A, aY);
ctx.lineTo(cx - f + A - 6, aY + 4);
ctx.strokeStyle = '#c9d1d9';
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.fillStyle = '#c9d1d9';
ctx.font = '13px "Segoe UI", Arial, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
ctx.fillText('a = ' + A.toFixed(0), cx - f + A/2, aY + 6);
// 偏心距 c = ea 标注
var cY = cy - b - 20;
ctx.beginPath();
ctx.moveTo(cx - f, cY);
ctx.lineTo(cx, cY);
ctx.strokeStyle = '#d29922';
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(cx - f + 5, cY - 4);
ctx.lineTo(cx - f, cY);
ctx.lineTo(cx - f + 5, cY + 4);
ctx.strokeStyle = '#d29922';
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(cx - 5, cY - 4);
ctx.lineTo(cx, cY);
ctx.lineTo(cx - 5, cY + 4);
ctx.strokeStyle = '#d29922';
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.fillStyle = '#d29922';
ctx.font = '13px "Segoe UI", Arial, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
ctx.fillText('c = ea = ' + f.toFixed(1), cx - f/2, cY - 4);
// 半短轴 b 标注
var bX = cx - f + A + 18;
ctx.beginPath();
ctx.moveTo(bX, cy);
ctx.lineTo(bX, cy - b);
ctx.strokeStyle = '#3fb950';
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(bX - 4, cy - 6);
ctx.lineTo(bX, cy);
ctx.lineTo(bX + 4, cy - 6);
ctx.strokeStyle = '#3fb950';
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(bX - 4, cy - b + 6);
ctx.lineTo(bX, cy - b);
ctx.lineTo(bX + 4, cy - b + 6);
ctx.strokeStyle = '#3fb950';
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.fillStyle = '#3fb950';
ctx.font = '13px "Segoe UI", Arial, sans-serif';
ctx.textAlign = 'left';
ctx.textBaseline = 'middle';
ctx.fillText('b = ' + b.toFixed(1), bX + 8, cy - b/2);
// 太阳(焦点)
ctx.beginPath();
ctx.arc(cx, cy, 7, 0, Math.PI * 2);
ctx.fillStyle = '#d29922';
ctx.fill();
ctx.strokeStyle = '#f0883e';
ctx.lineWidth = 1;
ctx.stroke();
ctx.fillStyle = '#f0f6fc';
ctx.font = '13px "Segoe UI", Arial, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'bottom';
ctx.fillText('太阳(焦点)', cx, cy - 12);
// 近日点标注
var periX = cx - f - A;
ctx.fillStyle = '#58a6ff';
ctx.beginPath();
ctx.arc(periX, cy, 4, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#8b949e';
ctx.font = '12px "Segoe UI", Arial, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
ctx.fillText('近日点 r = a(1-e) = ' + (A * (1 - e)).toFixed(1), periX, cy + 12);
// 远日点标注
var apX = cx - f + A;
ctx.fillStyle = '#58a6ff';
ctx.beginPath();
ctx.arc(apX, cy, 4, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#8b949e';
ctx.font = '12px "Segoe UI", Arial, sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
ctx.fillText('远日点 r = a(1+e) = ' + (A * (1 + e)).toFixed(1), apX, cy + 12);
// 地球(轨道上的点)
var angle = 0.4;
var ex = cx - f + A * Math.cos(angle);
var ey = cy - b * Math.sin(angle);
ctx.beginPath();
ctx.arc(ex, ey, 5, 0, Math.PI * 2);
ctx.fillStyle = '#58a6ff';
ctx.fill();
ctx.strokeStyle = '#1f6feb';
ctx.lineWidth = 1;
ctx.stroke();
ctx.fillStyle = '#58a6ff';
ctx.font = '12px "Segoe UI", Arial, sans-serif';
ctx.textAlign = 'left';
ctx.textBaseline = 'bottom';
ctx.fillText('地球', ex + 8, ey);
// 速度矢量
var vlen = 32;
var vx = -A * Math.sin(angle) * 0.7;
var vy = b * Math.cos(angle) * 0.7;
var vl = Math.sqrt(vx*vx + vy*vy);
if (vl > 0) {
vx = vx / vl * vlen;
vy = vy / vl * vlen;
}
ctx.beginPath();
ctx.moveTo(ex, ey);
ctx.lineTo(ex + vx, ey + vy);
ctx.strokeStyle = '#3fb950';
ctx.lineWidth = 2;
ctx.stroke();
ctx.beginPath();
var tipX = ex + vx, tipY = ey + vy;
var a1 = 0.3;
ctx.moveTo(tipX, tipY);
ctx.lineTo(tipX - vx * a1 + vy * a1 * 0.5, tipY - vy * a1 - vx * a1 * 0.5);
ctx.lineTo(tipX - vx * a1 - vy * a1 * 0.5, tipY - vy * a1 + vx * a1 * 0.5);
ctx.closePath();
ctx.fillStyle = '#3fb950';
ctx.fill();
// 信息面板
var roundE = Math.round(e * 10000) / 10000;
infoDiv.innerHTML =
'<span><span class="dot" style="background:#58a6ff"></span>椭圆轨道 e = ' + roundE.toFixed(4) + '</span>' +
'<span><span class="dot" style="background:#d29922"></span>偏心距 c = ' + f.toFixed(1) + '</span>' +
'<span><span class="dot" style="background:#3fb950"></span>b/a = ' + (b / A).toFixed(4) + '</span>' +
'<span>近日点 ' + (A * (1 - e)).toFixed(1) + ' / 远日点 ' + (A * (1 + e)).toFixed(1) + '</span>';
}
function update() {
var val = parseInt(slider.value);
var e = val / 100;
eDisplay.textContent = e.toFixed(2);
draw(e);
}
slider.addEventListener('input', update);
update();
})();
</script>
</body>
</html>