中土世界的前端

支援多種裝置開發的逐步操作說明

Daniel Isaksson
Daniel Isaksson
Einar Öberg
Einar Öberg

在關於 Chrome 實驗功能 A Journey Through Middle-earth第一篇文章中,我們著重於行動裝置的 WebGL 開發。在本文中,我們將探討建立其餘 HTML5 前端時,遇到的挑戰、問題及解決方案。

同一網站的三個版本

首先,我們將稍微說明如何調整這項實驗,讓電腦在電腦和行動裝置的螢幕都能完整顯示。

整個專案以非常「電影」的風格為基礎,而設計設計時,我們希望將遊戲體驗保持在橫向的固定畫面中,藉此保留電影的魔術效果。由於專案中大部分包含互動式小「遊戲」,因此也不適合讓這些項目溢出畫面。

我們可以參考到達網頁範例,瞭解如何針對不同尺寸調整設計。

他們剛剛把我們帶往到達網頁。
他們剛剛把我們帶往到達網頁。

該網站有三種模式:電腦、平板電腦和手機。不只是處理版面配置,但我們需要處理執行階段載入的資產,並新增各種效能最佳化設定。由於裝置的解析度比桌上型電腦和筆記型電腦高,但效能卻比手機低,因此要製定最終規則並不容易。

我們運用使用者代理程式資料偵測行動裝置,並透過可視區域大小測試來指定平板電腦 (645px 以上) 中的平板電腦。實際上,每種模式都會轉譯所有解析度,因為版面配置是以媒體查詢或 JavaScript 的相對/百分比定位為基礎。

由於在本例中,設計並非以格線或規則為基礎,而不同部分之間也很獨一無二,因此實際取決於具體的元素和情境,以及要使用的中斷點或樣式。我們不只使用完美的 sass-mixins 和媒體查詢設定了理想的版面配置,之後還需要根據滑鼠位置或動態物件新增特效,最後用 JavaScript 重新編寫所有內容。

我們也在 head 標記中新增含有目前模式的類別,以便我們在樣式中使用這些資訊,如以下範例所示 (在 SCSS 中):

.loc-hobbit-logo {

  // Default values here.

  .desktop & {
     // Applies only in desktop mode.
  }

 .tablet &, .mobile & {
   
   // Different asset for mobile and tablets perhaps.

   @media screen and (max-height: 760px), (max-width: 760px) {
     // Breakpoint-specific styles.
   }

   @media screen and (max-height: 570px), (max-width: 400px) {
     // Breakpoint-specific styles.
   }
 }
}

我們支援約 360x320 的所有尺寸,這對於打造沉浸式網路體驗相當艱難。以電腦版來說,我們之所以能夠顯示捲軸,必須設有最小尺寸,因為我們希望盡可能在較大可視區域中體驗網站。因此,我們決定讓行動裝置使用者能夠同時以橫向和直向模式,在互動式體驗功能上發揮最高成效,同時要求將裝置轉為橫向模式。抵禦了這一點,就在於它沒有像橫向模式的沉浸式體驗,但網站的縮放比例很不錯,所以我們仍然維持這點。

請務必注意,版面配置不得與功能偵測 (例如輸入類型、裝置螢幕方向、感應器等) 混用。這些功能可在上述所有模式下使用,應該橫跨所有模式。例如同時使用滑鼠和觸控手勢就是一個例子。Retina 以提高品質,但大多數表現則是另一款,但有時品質較差。舉例來說,畫布是 Retina 顯示器上 WebGL 體驗的一半解析度,否則需要轉譯像素數量的四倍

我們在開發過程中經常使用開發人員工具中的模擬器工具,尤其是在 Chrome Canary 中具備全新改良的功能和大量預設設定。這是快速驗證設計的好方法。不過,我們仍需要定期在實體裝置上進行測試。其中一個原因是,網站會改以全螢幕顯示。有垂直捲動的網頁在大多數情況下,會在捲動時隱藏瀏覽器使用者介面 (目前在 iOS7 上為 Safari 發生問題),但必須考量所有設定。我們也使用模擬器中的預設設定,並變更螢幕大小設定,以模擬可用空間的遺失情形。在監控記憶體用量與效能時,在實體裝置上進行測試也很重要

處理狀態

到達網頁之後,我們抵達中土世界的地圖。如果發現網址有變動,這個網站是單頁應用程式,會使用 History API 處理轉送

網站的每個部分都有一個繼承的功能,可沿用 DOM 元素、轉換、載入資產、處理等項目的樣板。當您探索網站的不同部分時,系統就會啟動各個部分,並從 DOM 和目前區段的資產中載入各個元素。

由於使用者可以點選瀏覽器的返回按鈕或透過選單隨時瀏覽,因此所有建立的內容都必須在某個時間點處理。逾時和動畫必須停止並捨棄,否則可能會造成不必要的行為、錯誤和記憶體流失。這項工作並非難事,尤其是在期限快到時,您需要盡快將所有內容都準備就緒。

展示門市據點

為了展現中土世界的美麗背景與角色,我們打造了一套圖片和文字元件的模組化系統,讓您可橫向拖曳或滑動。我們沒有在這裡啟用捲軸,原因是希望不同範圍的速度以不同速度顯示,例如在圖像序列中讓動作側移,直到短片播放完畢為止。

瑟蘭督伊廳堂
Thranduil's Hall

時間軸

著手開發時,我們不知道每個位置的模組內容。我們所知的,是想建立一種範本,以水平時間軸顯示不同類型的媒體和資訊,方便我們自由擁有六種不同的地點簡報,而不必重新建構六次。為了管理這一點,我們建立了時間軸控制器,並根據設定和模組行為處理模組平移。

模組和行為元件

我們新增支援的不同模組包括圖片序列、靜態圖像、視差場景、焦點轉移情境和文字。

視差情境模組採用不透明背景,含有自訂數量的圖層,監聽可視區域中的實際位置進度。

焦點轉移場景是視差值區的變化版本,另外,我們為每個圖層使用兩張圖片,讓這些圖層淡入和淡出來模擬焦點變化。我們嘗試使用模糊處理濾鏡,但費用還是很高,因此我們會等待 CSS 著色器產生效果。

TweenMax 外掛程式 Draggable 支援拖曳文字模組中的內容。您也可以使用滾輪或雙指滑動,垂直捲動畫面。請注意,throw-props-plugin 會在您滑動及放開時新增快速滑過式的物理動作。

模組也可以有不同行為,以元件組成。並且有各自的目標選取器和設定。「平移」可移動元素、縮放至縮放、資訊重疊熱點、視覺化測試的偵錯指標、起始標題疊加層、火焰圖層等等。這些控制項會附加至 DOM,或是在模組內控制其目標元素。

完成上述設定後,我們便能利用設定檔建立不同的位置,設定檔可定義要載入和設定不同類型的模組和元件的資產。

圖片序列

從效能和下載大小的模組中,最大的挑戰在於圖像序列。這個主題有深入的主題可供閱讀。在行動裝置和平板電腦上,我們會以靜態圖片取代這張圖片。如果我們想要在行動裝置上觀賞影片畫質,那該將太多資料解碼及儲存在記憶體中,是不容易的。我們嘗試了多種替代解決方案,首先使用背景圖片和 Sprite 工作表,但這會導致記憶體問題,且 GPU 需要在 Sprite 工作表之間切換時會發生延遲。然後,我們嘗試替換 img 元素,但速度也太慢。從 Sprite 工作表到畫布繪製影格的效果最好,因此我們開始進行最佳化。為了節省每個影格的運算時間,系統會使用臨時畫布預先處理要寫入畫布的圖片資料,然後利用 PuImageData() 進行解碼,然後解碼後就能使用陣列。接著,就可以將原始 Sprite 工作表進行垃圾收集處理,僅將最低限度的資料儲存在記憶體中。也許儲存未解碼的圖片實際上較少,但以這種方式拖曳序列可以提升效能。影格非常小,只有 640x400,但會在拖曳畫面顯示。停止移動時,系統會載入高解析度圖片並快速淡入。

var canvas = document.createElement('canvas');
canvas.width = imageWidth;
canvas.height = imageHeight;

var ctx = canvas.getContext('2d');
ctx.drawImage(sheet, 0, 0);

var tilesX = imageWidth / tileWidth;
var tilesY = imageHeight / tileHeight;

var canvasPaste = canvas.cloneNode(false);
canvasPaste.width = tileWidth;
canvasPaste.height = tileHeight;

var i, j, canvasPasteTemp, imgData, 
var currentIndex = 0;
var startIndex = index * 16;
for (i = 0; i < tilesY; i++) {
  for (j = 0; j < tilesX; j++) {
    // Store the image data of each tile in the array.
    canvasPasteTemp = canvasPaste.cloneNode(false);
    imgData = ctx.getImageData(j * tileWidth, i * tileHeight, tileWidth, tileHeight);
    canvasPasteTemp.getContext('2d').putImageData(imgData, 0, 0);

    list[ startIndex + currentIndex ] = imgData;

    currentIndex++;
  }
}

系統會使用 Imagemagick 產生 Sprite 工作表。這個 GitHub 上的簡易範例示範如何為資料夾中的所有圖片建立 Sprite 工作表。

建立模組動畫

如要將模組放在時間軸上 (時間軸的隱藏表示法,畫面外會顯示),請持續追蹤「播放頭」和時間軸的寬度。這項操作只要使用程式碼就能完成,但在開發及偵錯時,能以視覺化方式呈現。實際執行時,系統只會更新尺寸以設定尺寸。有些模組會填滿可視區域,有些模組則有自己的比例,因此要縮放內容及定位在所有解析度下,其實並不容易,導致所有畫面都要清楚顯示,不會裁切太多內容。每個模組都有兩個進度指標,一個代表畫面上顯示的可見位置,另一個用於模組本身的時間長度。製作視差動作時,通常很難計算物體的開始和結束位置,以便與所處的預期位置保持同步。最好能掌握模組進入檢視畫面的確切時間、播放內部時間軸,以及繼續顯示動畫後又消失的時間。

每個模組的頂端都有一點微妙的黑色圖層,這個圖層會調整其不透明度,因此當模組位於中心位置時,就會呈現完全透明。這個做法有助於您逐一專心處理一個單元,藉此提升使用體驗。

網頁效能

從運作正常的原型改為免卡頓的發布版本,意味著無須猜測瀏覽器會發生什麼事。Chrome 開發人員工具就是你的最佳幫手。

我們花了許多時間最佳化網站。為了創造流暢動畫效果,強制執行硬體加速是最重要的工具之一。此外,也請查看 Chrome 開發人員工具中的彩色資料欄和紅色矩形。有許多與這個主題相關的實用文章,您需要閱讀全部。移除略過影格所獲得的獎勵會立即生效,但又回歸時就會造成困擾。而且他們一定會做到。這是一個持續進行的程序,需要疊代。

我喜歡使用 Greensock 的 TweenMax 來替換房源、變形和 CSS。您可以在容器中,隨著您加入新層,以視覺化的方式呈現結構。請注意,新的轉換可以覆寫現有的轉換。如果只有補出 2D 值,則在 CSS 類別中強制硬體加速的 TranslateZ(0) 會替換為 2D 矩陣。如要在這類情況下保留圖層的加速模式,請在補間欄位中使用「force3D:true」屬性來建立 3D 矩陣,而非 2D 矩陣。組合 CSS 與 JavaScript 補間廣告素材設定樣式時,往往會讓人覺得十分難以置信。

請勿在不需要時強制使用硬體加速。當您需要硬體加速許多容器時,GPU 記憶體可能會快速填滿,並導致不需要的結果,尤其是在記憶體有較多限制的 iOS 上。如要載入較小的素材資源,然後透過 CSS 擴大素材資源的尺寸,同時停用行動模式下的一些效果,則大幅改善了整體效能。

記憶體流失是另一個需要提陞技能的欄位。在不同 WebGL 進行瀏覽時,系統會建立大量物件、材質、材質和幾何圖形。如果您離開時未準備好進行垃圾收集,並移除此部分可能會導致裝置在記憶體用盡後當機。

離開具有失敗處理函式的部分。
結束具有失效處理函式的部分。
好得多!
這樣更好!

為了找出流失問題,開發人員可以直接在開發人員工具的工作流程中記錄時間軸,並擷取堆積快照。如果可以篩除 3D 幾何圖形或特定資料庫等特定物件,這項功能會更簡單。在上面的例子中,我們發現 3D 場景仍位於周圍,而且儲存的幾何圖形並未清除。如果難以找出物件的所在位置,有一項實用的功能可檢視稱為「保留路徑」。只要在堆積快照中點選要檢查的物件,即可在下方的面板中查看相關資訊。以小型物件採用良好結構,有助於找出參照。

在 EffectComposer 中參照了場景。
在 EffectComposer 中參照了這個場景。

一般而言,在處理 DOM 之前,最好三思。如要這麼做,請思考效率。如果可以的話,請不要在遊戲迴圈中操控 DOM。將參照儲存在變數中,以便重複使用。如果您需要搜尋元素,請使用最短路徑,將策略容器的參照儲存在最近的上階元素中。

如果發生版面配置錯誤,則延遲讀取新增元素的維度。或者,如果發生版面配置錯誤,移除/新增類別時也要延遲。或是確認已觸發版面配置。有時瀏覽器會批次變更樣式,但會在下一個版面配置觸發後更新樣式。這有時會是重大問題,但這些原因可能出在某個原因,因此只要試著瞭解背後的運作機制,您就會得到許多收穫。

全螢幕

在適用情況下,您可以選擇透過 Fullscreen API,在選單中以全螢幕模式讓網站顯示全螢幕模式。但裝置也會決定以全螢幕模式顯示畫面。iOS 版 Safari 之前的入侵可讓您控制該機制,但現在已不辦法使用,因此您在製作非捲動網頁時,必須預先調整設計,確保其運作原理已不足夠。由於這項技術牽涉到許多網頁應用程式,因此我們會在日後更新時提供這項功能。

資產

實驗的動畫指示。
實驗的動畫操作說明。

我們的網站上有多種不同類型的素材資源,我們會使用圖片 (PNG 和 JPEG)、SVG (內嵌和背景)、Sprite 工作表 (PNG)、自訂圖示字型和 Adobe Edge 動畫。我們會將 PNG 用於素材資源和動畫 (Sprite 工作表),用於無法以向量為基礎的元素,否則我們會盡量使用 SVG。

向量格式並不會導致品質下降,即使我們縮放其比例也一樣。1 個檔案。

  • 檔案較小,
  • 我們可以為每個部分個別建立動畫 (最適合用於進階動畫)。舉例來說,我們會在縮小
  • 例如以 SVG HTML 代碼嵌入,或做為無須額外載入的背景圖片 (該圖片會與 HTML 網頁同時載入)。

圖示字體和 SVG 的優點,與 SVG 技術相輔相成,因此前者用於小元素,而不是可擴充向量圖形;這種小元素只需能夠變更顏色 (滑鼠懸停、使用中等) 即可。圖示也很容易重複使用,您只需設定元素的 CSS「content」屬性即可。

動畫

在某些情況下,使用程式碼為 SVG 元素建立動畫可能會相當耗時,尤其是在動畫需要在設計過程中進行大量變更時。為改善設計人員和開發人員之間的工作流程,我們使用 Adobe Edge 製作某些動畫 (遊戲開始前的操作說明)。動畫工作流程與 Flash 息息相關,這對於 Google 團隊有所幫助,但仍有一些缺點,尤其是將 Edge 動畫整合至素材資源載入程序中,因為它會附帶自己的載入器和實作邏輯。

我還是覺得有很長一段路要走,才能擁有完美在網路上處理素材資源和手工動畫的工作流程。我們很期待看到 Edge 等工具會如何發展。歡迎在留言中加入其他動畫工具和工作流程的建議。

結論

現在,當專案的所有部分都推出後,我們看看最終結果,我不得不認為現在的新型行動瀏覽器確實令我們印象深刻。一開始進行這項專案時,我們對於實現作業流暢、整合與效能的期望越來越低。這是我們很不錯的學習體驗,而且經過不斷疊代和測試後,我們更能掌握新式瀏覽器的運作方式。這正好是我們想要縮短這類專案的生產時間,例如從猜到如何。