Three.js – 部署與相容性
主題:WebGL 支援與 fallback
簡介
在現代前端開發中,Three.js 已成為打造 3D Web 應用的事實標準。它的核心是 WebGL,一個在瀏覽器內直接存取 GPU 的 API,能提供即時的圖形渲染效能。然而,並不是所有使用者的裝置或瀏覽器都原生支援 WebGL,或在某些環境(如舊版瀏覽器、企業內部防火牆、行動裝置)中會被限制。若不妥善處理這類相容性問題,網站的 3D 體驗很容易變成「白畫面」或「卡死」的情況,直接影響使用者留存與品牌形象。
本篇文章將說明 WebGL 支援的檢測方法、如何在不支援時提供 fallback(退化方案),以及在部署時需要留意的細節。內容以 Three.js 為例,適合剛接觸 3D Web 開發的初學者,也能為中階開發者提供實務上的最佳實踐。
核心概念
1. 為什麼要檢測 WebGL 支援?
- 裝置差異:舊手機、嵌入式系統可能沒有硬體加速或驅動程式不完整。
- 瀏覽器限制:某些企業環境會將 WebGL 相關的 API 禁用。
- 使用者體驗:即使支援,若 GPU 記憶體不足也會導致渲染失敗。
因此,在載入 Three.js 前先 檢測 是否能成功建立 WebGL 渲染上下文(WebGLRenderingContext),是避免白畫面的第一步。
2. 基本的 WebGL 支援檢測
Three.js 已內建 WebGLRenderer.isWebGLAvailable() 與 WebGLRenderer.isWebGL2Available() 方法,能快速回傳布林值。以下範例示範如何在頁面載入時檢測,並根據結果顯示不同的 UI。
// main.js
import * as THREE from 'three';
import { WEBGL } from 'three/examples/jsm/WebGL.js'; // 官方提供的檢測工具
function init() {
if (WEBGL.isWebGLAvailable()) {
// 正常啟動 Three.js 場景
startThreeScene();
} else {
// 顯示 fallback 內容
showFallbackMessage(WEBGL.getWebGLErrorMessage());
}
}
function startThreeScene() {
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 之後的場景、相機、物件設定...
}
function showFallbackMessage(message) {
const container = document.createElement('div');
container.style.cssText = 'font-family:Arial; color:#555; padding:20px;';
container.innerHTML = `
<h2>抱歉,您的瀏覽器不支援 WebGL</h2>
${message}
<p>建議您升級瀏覽器或使用支援的裝置。</p>
`;
document.body.appendChild(container);
}
// 立即執行
init();
重點:
WEBGL.getWebGLErrorMessage()會回傳一段已經排版好的 HTML,直接插入即可,省去自行撰寫錯誤訊息的時間。
3. Fallback 的兩大方向
| 方向 | 目的 | 常見技術 |
|---|---|---|
| Canvas 2D | 以 2D 繪圖方式呈現靜態或簡易動畫 | <canvas> + CanvasRenderingContext2D |
| CSS/HTML 替代 | 完全不使用 Canvas,改用圖片、SVG 或純文字說明 | <img>、<svg>、<div> |
3.1 Canvas 2D Fallback
如果只需要展示 靜態 3D 截圖或簡單的交互動畫,Canvas 2D 可以作為快速的退化方案。下面示範如何在檢測失敗時,載入一張事先渲染好的 PNG 圖片,並在滑鼠移動時切換圖層產生「偽 3D」感。
function showCanvas2DFallback() {
const canvas = document.createElement('canvas');
canvas.width = 800;
canvas.height = 600;
const ctx = canvas.getContext('2d');
const img = new Image();
img.src = 'fallback/scene-preview.png';
img.onload = () => ctx.drawImage(img, 0, 0);
// 假設有多張不同角度的截圖,根據滑鼠位置切換
const frames = ['scene-0.png', 'scene-1.png', 'scene-2.png'];
canvas.addEventListener('mousemove', (e) => {
const idx = Math.floor((e.offsetX / canvas.width) * frames.length);
const frameImg = new Image();
frameImg.src = `fallback/${frames[idx]}`;
frameImg.onload = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(frameImg, 0, 0);
};
});
document.body.appendChild(canvas);
}
技巧:將渲染好的圖片壓縮為 WebP 或 AVIF,可大幅降低載入時間,同時保留較好的畫質。
3.2 CSS/HTML Fallback
在資源極度受限的環境(如企業內部的舊版 IE),甚至連 Canvas 都無法使用。此時可改用 純 HTML 或 SVG 描述場景概念,並提供「下載完整體驗」的連結。
<div class="fallback-notice">
<h2>3D 內容暫不可用</h2>
<p>您目前的瀏覽器不支援 WebGL。以下為我們的模型概述:</p>
<svg width="300" height="200" viewBox="0 0 300 200">
<rect x="50" y="50" width="200" height="100" fill="#88c" stroke="#333" />
<circle cx="150" cy="100" r="40" fill="#f90" />
<!-- 其他簡易圖形 -->
</svg>
<a href="download/threejs-demo.zip" class="btn">下載離線示範</a>
</div>
提醒:CSS/HTML fallback 主要是 資訊 層面的補償,並不會提供互動式 3D,故應在 UI 設計上明確告知使用者「此為簡化版」或「請升級瀏覽器」。
4. 多層次的檢測策略
實務上建議採 三層檢測:
- 瀏覽器 API 檢測:使用
WEBGL.isWebGLAvailable()(或isWebGL2Available())快速判斷。 - 硬體資源檢測:利用
navigator.hardwareConcurrency、navigator.deviceMemory、window.devicePixelRatio等資訊,評估是否需要降低渲染品質(例如減少貼圖解析度或關閉陰影)。 - 效能測試:在載入第一幀時測量
renderer.info.render與performance.now(),若帧率低於 30 FPS,動態切換到 低品質 模式或 fallback。
以下是一段完整的檢測與自動降級範例:
import * as THREE from 'three';
import { WEBGL } from 'three/examples/jsm/WebGL.js';
function start() {
// 1️⃣ 基礎支援檢測
if (!WEBGL.isWebGLAvailable()) {
showFallbackMessage(WEBGL.getWebGLErrorMessage());
return;
}
// 2️⃣ 建立渲染器,同時檢查硬體資訊
const params = {
antialias: true,
powerPreference: 'high-performance' // 嘗試使用高效能 GPU
};
const renderer = new THREE.WebGLRenderer(params);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 3️⃣ 依硬體自動調整品質
const memory = navigator.deviceMemory || 4; // 單位 GB,預設 4GB
if (memory < 2) {
renderer.setPixelRatio(0.5); // 降低解析度
console.warn('Low device memory – 降低渲染解析度');
}
// 4️⃣ 建立簡單場景
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 1, 3);
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshStandardMaterial({ color: 0x0077ff });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(5, 10, 7);
scene.add(light);
// 5️⃣ 動態效能監測與降級
let lastTime = performance.now();
function animate() {
requestAnimationFrame(animate);
const now = performance.now();
const delta = now - lastTime;
if (delta > 1000 / 30) { // 低於 30 FPS
renderer.setPixelRatio(Math.max(0.3, renderer.getPixelRatio() - 0.1));
console.warn('FPS 低於 30 – 降低 pixelRatio');
}
lastTime = now;
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
}
start();
要點:
powerPreference可讓瀏覽器優先使用獨立顯示卡。navigator.deviceMemory只在 Chrome、Edge 等 Chromium 系列支援,但足以作為 參考。- 動態調整
pixelRatio可在不重新建立渲染器的情況下降低 GPU 負載。
常見陷阱與最佳實踐
| 陷阱 | 可能的結果 | 解決方案 |
|---|---|---|
直接使用 new THREE.WebGLRenderer() 而未檢測支援 |
在不支援的瀏覽器出現白畫面或 JavaScript 錯誤 | 使用 WEBGL.isWebGLAvailable() 包裝初始化邏輯 |
| 忽略跨域 (CORS) 問題 | 貼圖、HDRI、模型載入失敗,導致渲染中斷 | 確保伺服器回傳 Access-Control-Allow-Origin:*,或使用 crossOrigin="anonymous" |
| 過度依賴高解析度貼圖 | 低記憶體裝置 OOM (Out of Memory) | 提供多層次貼圖 (MIPMAP) 並根據硬體自動降級 |
| 未考慮螢幕 DPI | 在高 DPI 手機上畫面過小或模糊 | 使用 renderer.setPixelRatio(window.devicePixelRatio),但在低性能裝置適度降低 |
| 只提供單一 fallback(如僅 Canvas 2D) | 某些環境仍無法呈現(例如 IE8) | 同時提供 Canvas 2D 與 HTML/SVG 兩種退化方案 |
| 未清理資源 | 記憶體洩漏、渲染卡頓 | 在切換頁面或關閉模組時,呼叫 renderer.dispose()、geometry.dispose()、material.dispose() |
最佳實踐清單
- 先檢測,後渲染:永遠把 WebGL 檢測包在
try...catch或官方WEBGL工具中。 - 分層資源:使用
srcset、picture或 Three.js 的TextureLoader.setPath()提供不同解析度的貼圖。 - 漸進增強(Progressive Enhancement):核心功能(如互動式 3D)放在 WebGL,說明文字、靜態圖像放在 fallback。
- 使用 CDN + 本地備援:將 Three.js、GLTFLoader 等核心腳本放在 CDN,若 CDN 無法連線再回退本機檔案。
- 加入使用者指引:在 fallback 中提供「升級瀏覽器」或「啟用硬體加速」的說明連結,提升轉換率。
- 效能監控:部署後使用 Google Analytics 的自訂維度或 Web Vitals 收集
webglSupported、fps等指標,持續優化。
實際應用場景
| 場景 | 為何需要 fallback | 建議實作方式 |
|---|---|---|
| 電商產品展示(3D 旋轉模型) | 部分顧客使用舊手機或 Safari 隱私模式,WebGL 可能被封鎖 | 先顯示 Canvas 2D 版的多角度截圖,點擊「進階觀看」時再嘗試載入 WebGL;若仍失敗,顯示靜態渲染圖與說明文字 |
| 教育平台的互動課程 | 教室內使用的投影機或老舊 Windows PC 可能不支援 WebGL | 使用 HTML/SVG 替代的教學圖示,並在右側提供「下載離線教材」的 PDF;同時在支援環境提供完整 Three.js 互動 |
| 行銷活動的 AR/VR 體驗 | 手機瀏覽器的 WebGL2 支援度較低,且 AR 需要額外權限 | 先檢測 WebGL2,若無則降級為 WebGL1(Three.js 自動處理)或直接呈現 2D 動畫;同時提供 QR code 讓使用者下載原生 App |
| 企業內部儀表板 | 企業防火牆會阻擋 WebGL,且安全政策要求所有外部資源必須審核 | 使用本地部署的 Three.js,並在檢測失敗時顯示 SVG 版的圖表或純文字報表;保留 API 呼叫的備援 JSON 資料來源 |
這些案例說明,fallback 不只是技術需求,更是提升使用者體驗的關鍵。透過分層設計,即使在最差環境下也能保證資訊可讀,進而提升整體轉換率與品牌形象。
總結
- WebGL 為 Three.js 的基礎,但在真實世界中並非所有裝置都能使用。
- 利用 Three.js 官方的
WEBGL檢測工具,配合 硬體資源與效能測試,可在載入前判斷是否需要 fallback。 - Canvas 2D 與 HTML/SVG 是兩條常見的退化路徑,選擇哪一條取決於預算、時間與目標使用者族群。
- 謹記 三層檢測、資源分層、效能監控 與 漸進增強 的最佳實踐,才能在多樣化的瀏覽器與裝置環境中提供穩定且流暢的 3D 體驗。
透過本文的概念與範例,你現在可以在自己的 Three.js 專案裡,安全地部署 WebGL,同時為不支援的使用者提供貼心的 fallback,讓每一次的 3D 體驗都不留遺憾。祝開發順利! 🚀