iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0
Modern Web

從 Web 到宇宙:Three.js × WebGL 互動式太陽系與光影實作系列 第 24 篇

Day 24 - UI Controls:替太陽系加入控制面板

  • 分享至 

  • xImage
  •  

目前的太陽系已經有不少功能,但幾乎所有效果都是:
程式啟動
全部顯示
使用者只能觀看

所以從今天開始,我們要正式進入 User Interaction,
今天先建立一個簡單的控制面板,讓使用者可以自己決定要不要顯示:

行星軌道
金星大氣層
星空背景

由於目前專案程式碼已逐漸增加,從 Day 23 開始文章只列出當日新增或修改的程式碼。
完整整合版將於 Day 30 最終完成時提供。

一、今天完成目標

今天會完成:

  • 建立 Solar System Controls 控制面板
  • 使用 HTML Checkbox 控制 Three.js 物件
  • 顯示/隱藏行星軌道
  • 顯示/隱藏 Moon Orbit
  • 顯示/隱藏 Venus Atmosphere
  • 顯示/隱藏 Starfield
  • 使用 .visible 控制 Three.js Object
  • 將 Planet Orbit 儲存到 Array

二、本日修改摘要

檔案 修改內容
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"> 就是勾選框

四、checked

我們在每個 Checkbox 都加入:
checked

代表網頁一開始:
顯示軌道
金星大氣層
星空背景

都是開啟狀態。
因為目前 Three.js 場景本來就會顯示這些物件。
如果拿掉checked , Checkbox 一開始就會變成 。
但是 Three.js 物件仍然存在。
所以今天讓 HTML Checkbox 狀態與 Three.js 畫面狀態保持一致。

五、控制面板 CSS

接著在 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(...);

讓背景帶一點透明。

六、今天最重要的新觀念:visible

Three.js 裡大部分的:

Mesh
Line
Points
Group

都繼承自THREE.Object3D 因此都有.visible這個屬性。
例如:

stars.visible =
    false;

代表 Starfield 隱藏
改成:

stars.visible =
    true;

代表 Starfield 顯示
物件並沒有被刪除只是暫時不 Render 因此非常適合今天的 UI。

七、控制 Starfield

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

八、控制 Venus Atmosphere

之前我們已經特別保留:

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 的差異。

九、Planet Orbit 需要稍微修改

Starfield 跟 Venus Atmosphere 都已經有變數可以直接控制。
但 Day 23 的 Orbit 建立方式是:

planetData.forEach(
    (data) => {

        ...

        const orbit =
            new THREE.LineLoop(
                orbitGeometry,
                orbitMaterial
            );

        scene.add(
            orbit
        );

    }
);

問題是 forEach 結束 orbit 變數就無法從外面取得
所以今天要新增:

const planetOrbits =
    [];

十、保存每一條 Planet Orbit

在建立 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。

十二、Moon 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 Control 程式

最後新增:

// ========================
// 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
從今天開始,我們的太陽系就正式從可以看變成可以操作。
附圖:
image
image

Day 25 預告

「Animation Controls:Play、Pause × Speed」

今天的 UI 只能控制:
顯示/隱藏
Day 25 要開始控制:
時間

Solar System Controls

顯示軌道
金星大氣層
星空背景

────────────────

▶ Play / Pause

Speed
1.0x
──────●──────

控制面板會繼續增加這會是我們下一個很重要的互動功能。


上一篇
Day 23 - Saturn Ring × Starfield:加入土星環與宇宙星空
系列文
從 Web 到宇宙:Three.js × WebGL 互動式太陽系與光影實作 共 24 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言