做了將近兩個月終於把新的 Landing Page 弄上線,講講這次中間的過程和學到的新東西
Landing Page:
Twitter: @fetalkpodcast
Threads: @fetalkpodcast
Discord: https://discord.gg/BjRASf374X
Email: fetalk.podcast@gmail.com
上禮拜我們剛 deploy 完公司網站包含新的 landing page,如果你第一次聽的話我在一間做電動車的公司擔任 Senior Software Engineer,三個月前公司發表下一代車款而今天現有車子發表改款改善性能、加了新功能、而最重要的內部構造重新設計降低製造成本,而我我和我的組的工作就是把網站上的 landing page 做改版,理論上是更新資料和圖片但畢竟是改款所以當然是順便連網頁一起改,印象中大概是四月時候就跟 designer 在做早期討論看 prototype 長什麼樣子畫面想要怎麼呈現,在第十三集時候講過說這時候就是跟設計師討論什麼是有可行的和怎麼做比較好,比如看到別的網站有個功能我們可以利用問我們能不能做,我們得花時間去做 spike 和研究看 pros and cons 和如何開發尤其是效能和能不能在不同設備和 viewport 下都能很好的呈現加上這個 codebase 本來不是我們的也得看能不能支援,大概這樣來來回回很久終於弄了一版是我們可以開始開發的版本,不過鑑於之前的經驗我們知道設計常常會改變所以我們也有了心理準備,不過這集比較想聊聊我們 landing page 比較 technical 的東西
先講最大方向的每個功能都會用到的就是內容也就是文字圖片和影片,我們幾乎每一行文字和其他 asset 都不在 codebase 裡面,我們把文字放在一個 CMS 裡面然後 asset 放在 Cloudinary 一個 image CDN 上面,這樣把內容和 codebase 分開讓我們需要內容更新的時候不需要整個重新 build 一遍而只要更新 CMS 和 Cloudinary 就好,分開另一個好處是翻譯人員可以直接在 CMS 裡面做翻譯就好,整體這大大減少後期因為內容更動而要做 build 的時間,而 image CDN 一方面幫我們 host 我們的圖片和影片一方面也可以透過改動網址修改 param 來直接對圖片做修圖的動作或是改 format 和 quality,比如我們上傳 jpeg 但我們網址其實都使用了 webp 的圖片達到效能的優化或是在 desktop 和 mobile 之間修改 quality 這樣影片在 desktop 看很漂亮但在手機上看可能差一點點可是確保檔案很小可以很快的下載。
畢竟這是新款發表,公司設定的 embargo 時間是 6/6 早上九點,意思是各大媒體這時間之後才可以發新聞稿而我們也遵照這個時間做 deploy,Cloudinary 有個功能是可以設定我們這些圖片在幾點之後才可以 access,因為我們有些步驟是前一天晚上做的所以以防萬一被人猜到網址是什麼所以鎖了一個時間,若提前知道大家看到的會是 401,這功能蠻好的。
網站其他頁面做動畫的 library 本來是用 GSAP,這一個 project 是我們這個組第一次碰到需要用到 animation 的 project 而我們第一次看到 GSAP 覺得這很 powerful 但有點臃腫而且 project 一開始有些時間所以我們開始看其他 library 比如 Framer Motion,Framer Motion 老實說可能沒有 GSAP 強大因為沒有很多底層的 API 比較偏 abstraction 都打包好了但有 React 相關的 component 和 hooks 這在整合上面其實變的很容易,常常就是拿他們的 hook 和他們的 motion component 東西就會動了,我們網頁上面有個 parallax 就是滾動的時候前面的字和後面的背景滾動速度不一致給人一種 3D 立體感的感覺,還有一個中間有個 section 是滾動的時候背景要根據滾動的位置做旋轉和 zoom out,這些都是 Framer Motion 可以幫忙做的,其他比如要 animate 距離、顏色、或 component appear and disappear 這些 Framer Motion 都有提供範例,其實網頁還有一個是 Framer Motion 做的不過等等動畫環節一起說。
畢竟這是 landing page 就是要介紹車子有什麼功能和特點,所以需要很多內容去做介紹,設計師選擇不要一頁很長的頁面而是用 carousel 的方式去讓使用者去點,也算是把每一個內容都隔開這樣,我覺得我同事做的方式蠻厲害的而且很系統化,他先照著 figma 把每一區塊的 component 做出來,然後定義了一個 data model 包含這一頁是什麼 type 和每個 type 裡面的內容需要什麼都定義好,最後把這整個資料喂給 component 就會自動渲染好,這麼做好處就是這些 content modal UI 只要透過更改內容也就是 JSON object 就能更改 UI,這在後期他們需要做些修改和更新內容的時候非常有幫助。
fun facts 這 modal 早期設計師想要當我們點開一個 modal 的時候,當使用者一直滑滑到下面的時候可以自動下載下一個 modal 的資料然後外面那個 carousel 需要滑到下一個 item,這被我們反對因為有點麻煩還得 track position 什麼的而且 position 的定義也不清楚,加上如果要這樣那不如不要 carousel 直接全部放在同一頁就好,就變得有點 confusing。
我們網頁上面第二個 section 是我們所謂的 360,這部分是我同事做的我真覺得這個他可以在短時間內把這個生出來真的很屌,這個 section 有兩個 feature,一個是畫面有一台車而當使用者 scroll 的時候需要跟著 scroll 的進度而旋轉這台車,第二個是用這同一台車使用者可以用 drag 拖拉的方式去旋轉這台車讓使用者可以在網頁上 360 度的欣賞這台車,類似的功能我們在另一頁有但只有根據 scroll 的時候讓它轉,乍看之下看起來像動畫但其實是我們下載了比如 50 張圖片來代表每一幀的畫面,利用 GSAP 和 scroll 來偵測我滑到哪裡應該要顯示哪一張圖片,我們一開始挺反對這麼做的因為那一頁其實效能很差而初期認為是這個原因畢竟顯示一個 feature 我們要先下載 50 張圖片,雖然這麼說我們還是去做研究看看 framer motion 怎麼實現這個,運氣很好網上有人有 sample 所以一天之內就把 MVP 生出來,這一版是在 JavaScript 用新增一個 array 裡面包含所有的圖片路徑然後用 Image class 去下載這些圖片,最後把每一個圖片變成一個 framer motion 的 frame,然後根據 mouse down 和 move event 和偵測你往左和右多少決定顯示哪一個 frame,一開始是遇到滿多問題的比如這塊 algorithm 怎麼寫、scrolling 和 dragging 衝突、不同設備的優化。
同一時間我負責的 Drive Mode 也有類似的需求但我去嘗試怎麼用影片的方式作呈現,講一下 context Drive Mode 是車子本身的功能可以透過調解車子高度避震等等來適應不同的路面比如道路模式或雪地模式或甩尾模式等等總共九種,這次車子改版想要強調這個所以算是網頁上重點功能之一,因應這次改版,我們 infotainment team 特別做了新的 Drive Mode 動畫,因為車子內部系統畫面是用 Unreal Engine 做的所以想當然這動畫也是用 Unreal Engine 畫的,我們本來想說有沒有辦法直接從 Unreal Engine export 成 WebGL 或是其他 library 看得懂的東西,結果舊版的好像可以但新版的沒有了,沒有其他官方正式作法把這些 export 出來所以這方法就算了,那既然同事在弄另一個方法我就看看影片方式可不可行,我的功能只要知道每一個 drive mode 在哪一針也就是在哪一秒,我只要在 time updae event 或是自己寫 setInterval 偵測是不是到我要的那個畫面叫 pause 就好了,一開始滿成功的畢竟就是影片,後來遇到第一個問題是我們除了正常播放到下一個 drive mode 我們也希望回到上一個 mode 也想看看影片能不能倒過來放,瀏覽器其實沒有一個 native 的方式可以讓影片 reverse,Safari Video API 可以把 playbackRate 調成 -1 但這不是統一的 API 所以不要用,沒有 native 方式我只能透過 setInterval 和透過更新 currentTime 來手動播放影片,前面兩個 Drive Mode 這樣播放時 ok 的但越後面就越卡甚至看起來沒有更新,研究了一下才了解影片不是只是有每一幀的畫面而是幾幀比較重要的剩下的就是 frame 之間的變化,如果影片只儲存每一幀的完整畫面那這個影片檔案會變超大但如果是 frame pixel 怎麼變化就小很多,那現今 video decoder 通常都是 optimize 做 forward playback 從重要畫面推算現在這個時間應該要畫出什麼畫面,若要做 reverse 那 decoder 要重新計算,如果設備 CPU 夠快和 memory 夠大那 ok 但我不能預設大家都有很厲害的設備跑網站,你如果發現看影片然後要回放或是直接點一個時間點結果播放很慢的話就是類似這個原因,鑑於這個發現我就不做 reverse 的功能了,當然也有其他方法比如控制多個影片等等但個人覺得在現有的時間做這些有點複雜。
第二點我發現我的影片在 Android 上面跑不出來,我先說這邊是我自己忘記透過 Cloudinary 優化影片,如果一開始就優化了就不會有這個問題了但因為這樣學到不同設備有不同的限制尤其是 bitrate,通常 bitrate 越高代表資訊量愈大也通常代表畫面越清楚顏色越多之類的,我雖然沒有找到實際上 Android 最多可以支援多少但他們有個推薦值最多到 2 Mbps 到 10 Mbps,這數字取決於你的影片解析度和用哪種 encoder 比如 h.264 或是 VP8 VP9,我的影片將近 50 Mbps,感覺應該是爆了,這時候我才發現沒有用到 Cloudinary,把長寬設定完 bitrate 降下來就能播放了。
我們花了一些時間討論應該怎麼部署、什麼步驟要先做和什麼時候做以確保在網站沒有 downtime 的情況下也不會像上次那樣提前 leak 資料做更新,上線前一天我們 CICD 一直很固執不讓我們 deploy,後來做了兩件事情才順利 deploy 就是第一先設定把 CICD 用的 memory 調高,通常是這個原因哈哈,第二個是我們有一個類電商的東西在我們官網上面,然後不曉得為什麼那個電商網站是 SSG 的,我們上面 product 超多每一款還不同顏色什麼的,把那個 SSG 關掉之後就順利了,感覺應該這個關掉 memory 就不用增加了 but the more the merrier,這時候凌晨兩點大家趕快睡覺。
前面雖然說 Framer Motion 好像挺不錯但後來看了一下對照一下總覺得 GSAP 雖然複雜但資料多容易查而 Framer Motion 常常沒有 examples 或是都會變成要我們用 Framer 做,所以除非你做的東西剛好是 Framer Motion examples 裡面有的要不然可能挺麻煩的。
影片優化部分或是能不能 reverse seek,總覺得可能跟當初他們 export video 時候設定有關係,因為我還遇到其他問題比如 mp4 Safari 有時候會空白或是變成 webm 時候 Safari 不能透過 currentTime 做搜尋但我找到的其他影片可以。
總結來說我覺得從一開始的設計稿到最後成果然後如期上線真的挺不容易的,感謝同事和設計師以及其他 stakeholders 的互相幫忙和諒解。