跳至主要內容
所有文章

首頁那張圖是畫的,不是拍的

本來要放一張圖庫照片。後來改成用 SVG 直接畫在伺服器回傳的 HTML 裡——不是為了風格,是為了三件具體的事:它壞不掉、它不用等網路、它會跟著主題換色。順便講一個把整張圖弄不見的 bug。

閱讀約 2 分鐘分類手工

第一版首頁的主視覺是一張圖庫照片。海邊、逆光、有點顆粒感的那種。它沒有錯,只是它跟旁邊那句標題毫無關係——那句話講的是「外框是我們做的(網站、App),裡面活著的是 AI」,而照片講的是有人去了海邊。

所以那張照片被換掉了。現在那個區塊是一段 SVG,每一個座標都寫在伺服器回傳的 HTML 裡。

三個理由,都不是美感

它壞不掉。 JS 沒載到、hydration 失敗、分頁在背景被瀏覽器凍結——讀者看到的還是一張畫完的圖,只是少了環境動態。這是這個 repo 的硬規矩:伺服器回傳的 HTML 裡不准出現 opacity: 0,也不准出現長度為零的路徑。靜止狀態就是備援,所以靜止狀態必須是成品。

它比它取代的照片快。 inline SVG 跟著文件一起畫出來,所以首屏最大內容繪製不用再等一張網路圖片把自己安頓好。

它沒有自己的顏色。 每一個 fill 和 stroke 都是 palette token。深色模式不需要另外做一張圖,因為它根本不是圖片,它是會跟著主題走的一段標記。

靜止狀態就是備援,所以靜止狀態必須是成品。

這條規矩是撞出來的,不是想出來的

那個把整張圖弄不見的 bug

動畫層是 anime.js。第一版寫法很直覺:讓它去動圓的 r,再用 svg.createDrawable 做線條描繪。

兩個都會炸,而且是同一種炸法。

anime.js 把 r 當成 attribute 在處理,補間結束時它會把那個 attribute 整個拿掉——於是畫面上剩下一堆沒有半徑的圓。createDrawable 更乾脆:它被呼叫的那一瞬間就把每一條路徑改寫成零長度,準備從頭描起。

只要 frame 一直來,這兩件事都看不出問題。frame 一停,就是一個空盒子。而這張圖存在的目的,正好就是為了避免空盒子。

順帶一提:SVG 的 transform: scale() 預設是以文件原點為基準的,所以一個圓被 scale 之後會飛到畫布外面,而不是在原地長大。transform-box: fill-box 才會讓它以自己為基準。這一行也是撞出來才知道要加的。

什麼時候還是該用照片

不是所有東西都該畫。產品實拍、現場、人——這些畫出來會假。這張圖能畫,是因為它本來就是抽象的:它是標題那句話的圖解,不是某個東西的照片。

它也標了 aria-hidden。它是裝飾性的環境圖形,旁邊的標題已經把話講完了,再讓螢幕閱讀器唸一次「線框裡面有一格一格的點」只是噪音。

接著讀

心裡已經有想法了嗎?

說說你想做什麼,我們會老實告訴你,這件事適不適合交給我們。