本文 AI 產出,尚未審核

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 都無法使用。此時可改用 純 HTMLSVG 描述場景概念,並提供「下載完整體驗」的連結。

<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. 多層次的檢測策略

實務上建議採 三層檢測

  1. 瀏覽器 API 檢測:使用 WEBGL.isWebGLAvailable()(或 isWebGL2Available())快速判斷。
  2. 硬體資源檢測:利用 navigator.hardwareConcurrencynavigator.deviceMemorywindow.devicePixelRatio 等資訊,評估是否需要降低渲染品質(例如減少貼圖解析度或關閉陰影)。
  3. 效能測試:在載入第一幀時測量 renderer.info.renderperformance.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 2DHTML/SVG 兩種退化方案
未清理資源 記憶體洩漏、渲染卡頓 在切換頁面或關閉模組時,呼叫 renderer.dispose()geometry.dispose()material.dispose()

最佳實踐清單

  1. 先檢測,後渲染:永遠把 WebGL 檢測包在 try...catch 或官方 WEBGL 工具中。
  2. 分層資源:使用 srcsetpicture 或 Three.js 的 TextureLoader.setPath() 提供不同解析度的貼圖。
  3. 漸進增強(Progressive Enhancement):核心功能(如互動式 3D)放在 WebGL,說明文字、靜態圖像放在 fallback。
  4. 使用 CDN + 本地備援:將 Three.js、GLTFLoader 等核心腳本放在 CDN,若 CDN 無法連線再回退本機檔案。
  5. 加入使用者指引:在 fallback 中提供「升級瀏覽器」或「啟用硬體加速」的說明連結,提升轉換率。
  6. 效能監控:部署後使用 Google Analytics 的自訂維度或 Web Vitals 收集 webglSupportedfps 等指標,持續優化。

實際應用場景

場景 為何需要 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 2DHTML/SVG 是兩條常見的退化路徑,選擇哪一條取決於預算、時間與目標使用者族群。
  • 謹記 三層檢測、資源分層、效能監控漸進增強 的最佳實踐,才能在多樣化的瀏覽器與裝置環境中提供穩定且流暢的 3D 體驗。

透過本文的概念與範例,你現在可以在自己的 Three.js 專案裡,安全地部署 WebGL,同時為不支援的使用者提供貼心的 fallback,讓每一次的 3D 體驗都不留遺憾。祝開發順利! 🚀