目前的太陽系已經有不少功能,但幾乎所有效果都是:
程式啟動
全部顯示
使用者只能觀看
所以從今天開始,我們要正式進入 User Interaction,
今天先建立一個簡單的控制面板,讓使用者可以自己決定要不要顯示:
行星軌道
金星大氣層
星空背景
由於目前專案程式碼已逐漸增加,從 Day 23 開始文章只列出當日新增或修改的程式碼。
完整整合版將於 Day 30 最終完成時提供。
今天會完成:
| 檔案 | 修改內容 |
|---|---|
| index.html | 新增控制面板 |
| style.css | 新增控制面板樣式 |
| main.js | 儲存 Orbit、取得 Checkbox、控制 .visible |
| Starfield | 維持目前的 100 ~ 140 生成範圍 |
今天的控制面板會先長得像:
Solar System Controls
顯示軌道
金星大氣層
星空背景
首先修改 index.html。
在原本:
<div class="title">
<h1>從 Web 到宇宙</h1>
<p>DAY 24|UI Controls</p>
</div>
下面新增:
<div class="control-panel">
<h2>
Solar System Controls
</h2>
<label>
<input
type="checkbox"
id="orbitToggle"
checked
>
顯示軌道
</label>
<label>
<input
type="checkbox"
id="venusToggle"
checked
>
金星大氣層
</label>
<label>
<input
type="checkbox"
id="starToggle"
checked
>
星空背景
</label>
</div>
這裡使用的是普通 HTML:<input type="checkbox"> 就是勾選框
我們在每個 Checkbox 都加入:
checked
代表網頁一開始:
顯示軌道
金星大氣層
星空背景
都是開啟狀態。
因為目前 Three.js 場景本來就會顯示這些物件。
如果拿掉checked , Checkbox 一開始就會變成 。
但是 Three.js 物件仍然存在。
所以今天讓 HTML Checkbox 狀態與 Three.js 畫面狀態保持一致。
接著在 style.css 新增:
.control-panel {
position: fixed;
top: 30px;
right: 30px;
z-index: 20;
width: 230px;
padding: 18px;
background: rgba(
15,
15,
25,
0.8
);
border: 1px solid
rgba(
255,
255,
255,
0.15
);
border-radius: 12px;
backdrop-filter:
blur(10px);
}
.control-panel h2 {
margin:
0
0
16px;
font-size: 16px;
}
.control-panel label {
display: block;
margin-top: 12px;
font-size: 14px;
cursor: pointer;
}
.control-panel input {
margin-right: 8px;
cursor: pointer;
}
這樣控制面板會固定在右上角
而且使用:
background:
rgba(...);
讓背景帶一點透明。
Three.js 裡大部分的:
Mesh
Line
Points
Group
都繼承自THREE.Object3D 因此都有.visible這個屬性。
例如:
stars.visible =
false;
代表 Starfield 隱藏
改成:
stars.visible =
true;
代表 Starfield 顯示
物件並沒有被刪除只是暫時不 Render 因此非常適合今天的 UI。
Day 23 我們已經建立:
const stars =
new THREE.Points(
starGeometry,
starMaterial
);
目前 Starfield 使用 2000 個 Points,而你現在把星星距離改成集中在太陽系中心
約 100 ~ 140 的範圍。原本 Day 23 的 Points 架構則維持不變。
你現在的半徑設定改成:
const radius =
100 +
Math.random() *
40;
因此 100 + 0 ~ 40 = 100 ~ 140
接著取得 Checkbox:
const starToggle =
document.getElementById(
"starToggle"
);
監聽:
starToggle.addEventListener(
"change",
() => {
stars.visible =
starToggle.checked;
}
);
這裡 starToggle.checked 會回傳 true / false
所以剛好可以直接交給 stars.visible
之前我們已經特別保留:
let venusAtmosphere =
null;
這次終於正式派上用場。
取得 Checkbox:
const venusToggle =
document.getElementById(
"venusToggle"
);
然後:
venusToggle.addEventListener(
"change",
() => {
if (
venusAtmosphere
) {
venusAtmosphere.visible =
venusToggle.checked;
}
}
);
勾選 ☑ 金星大氣層 venusAtmosphere.visible = true
取消 ☐ 金星大氣層 venusAtmosphere.visible = false
就能直接比較有無 VenusAtmosphere 的差異。
Starfield 跟 Venus Atmosphere 都已經有變數可以直接控制。
但 Day 23 的 Orbit 建立方式是:
planetData.forEach(
(data) => {
...
const orbit =
new THREE.LineLoop(
orbitGeometry,
orbitMaterial
);
scene.add(
orbit
);
}
);
問題是 forEach 結束 orbit 變數就無法從外面取得
所以今天要新增:
const planetOrbits =
[];
在建立 Orbit 後:
const orbit =
new THREE.LineLoop(
orbitGeometry,
orbitMaterial
);
除了:
scene.add(
orbit
);
再加入:
planetOrbits.push(
orbit
);
這樣就可以一次控制所有軌道。
取得:
const orbitToggle =
document.getElementById(
"orbitToggle"
);
接著:
orbitToggle.addEventListener(
"change",
() => {
planetOrbits.forEach(
(orbit) => {
orbit.visible =
orbitToggle.checked;
}
);
}
);
這樣把顯示軌道的勾取消,就可以一次隱藏八條 Planet Orbit。
我們還有一條 moonOrbit
所以在 Orbit Toggle 裡再加入:
moonOrbit.visible =
orbitToggle.checked;
最後完整會變成:
orbitToggle.addEventListener(
"change",
() => {
planetOrbits.forEach(
(orbit) => {
orbit.visible =
orbitToggle.checked;
}
);
moonOrbit.visible =
orbitToggle.checked;
}
);
這樣把顯示軌道的勾取消,就會同時隱藏 8 條 Planet Orbit + Moon Orbit。
最後新增:
// ========================
// UI Controls
// ========================
const orbitToggle =
document.getElementById(
"orbitToggle"
);
const venusToggle =
document.getElementById(
"venusToggle"
);
const starToggle =
document.getElementById(
"starToggle"
);
orbitToggle.addEventListener(
"change",
() => {
planetOrbits.forEach(
(orbit) => {
orbit.visible =
orbitToggle.checked;
}
);
moonOrbit.visible =
orbitToggle.checked;
}
);
venusToggle.addEventListener(
"change",
() => {
if (
venusAtmosphere
) {
venusAtmosphere.visible =
venusToggle.checked;
}
}
);
starToggle.addEventListener(
"change",
() => {
stars.visible =
starToggle.checked;
}
);
現在我們第一次把 HTML 和 Three.js 真正串起來。
流程是:
使用者
↓
Checkbox
↓
change Event
↓
JavaScript
↓
.visible
↓
Three.js Object
例如:
☐ 星空背景
↓
starToggle.checked
↓
false
↓
stars.visible = false
↓
Starfield 消失
這就是 Modern Web 裡很重要的 UI + 3D Scene 互動。
今天沒有加入新的星球。
但這其實是一個很重要的更新。
目前控制面板已經可以控制:
Moon Orbit
Venus Atmosphere
Starfield
今天最重要的新東西就是 object.visible
我們並沒有刪除物件,只是決定它 Render 或不 Render
從今天開始,我們的太陽系就正式從可以看變成可以操作。
附圖:

「Animation Controls:Play、Pause × Speed」
今天的 UI 只能控制:
顯示/隱藏
Day 25 要開始控制:
時間
Solar System Controls
顯示軌道
金星大氣層
星空背景────────────────
▶ Play / Pause
Speed
1.0x
──────●──────
控制面板會繼續增加這會是我們下一個很重要的互動功能。