Washing Line Hero
網站的第一個畫面:頂端是品牌的導覽列與按鈕,旁邊是大型標題(其中一個詞組使用強調色)、一行介紹文字、一或兩個按鈕與一行備註,另一側則是橫跨天空的曬衣繩。繩上掛著一塊由木夾固定的棉布,上面印有品牌自己的海報:名稱、一行小字、兩個大字、規格欄與一個小型長條圖。微風不時吹起布料,雲朵在後方飄移。
這個區塊設計成可觸摸的。將游標移到布料上,指尖會壓出凹陷。按壓並拖曳,布料會跟著你的手移動:拉起一角、折疊到繩子上、朝自己方向拉伸。放手後它會落下、搖晃並恢復靜止。在手機上,手指也能進行相同操作,且按住布料時頁面不會捲動。布料是透過模擬產生的(由拉伸、剪切與彎曲約束連結的粒子網格,以固定 120 Hz 頻率更新),因此無論從何處抓取,它都會像真實織物般反應,而非播放預錄動畫。
你可以在 hero.json 中描述英雄區塊與印刷內容。建置腳本會檢查所有項目,並輸出 out/index.html(單一檔案形式的完整頁面)與 out/embed.html(可貼到現有網站中的區段)。由於模擬從靜止狀態開始以固定時間步長執行,record.mjs 可以逐幀驅動它:在影片中,一個繪製的游標會拉起一角、鬆開,然後用指尖橫掃布料,因此影片能展示訪客可進行的操作,且在任何機器上都相同。
它不是什麼:布料不會撕裂、自體交纏或從繩上吹落(沒有自碰撞,因此若用力摺疊使其穿過自身,它會穿過並落回),且印刷內容是由頁面根據你提供的文字繪製,而非上傳的照片。
你需要準備
- Node.js 18 或更新版本。無需安裝其他套件。
- Chrome、Chromium 或 Edge,用於檢查與錄製(腳本會自動尋找;若找不到請設定
CHROME_PATH)。 - ffmpeg,用於影片處理(
brew install ffmpeg、winget install ffmpeg、apt install ffmpeg;或設定FFMPEG_PATH)。 - 頁面開啟時需有網路連線:three.js 與字型會從 jsDelivr 載入,使用固定版本與完整性雜湊。若無網路,訪客會看到靜態圖片(相同的繩子與布料,平面繪製)與系統字型;頁面仍會正常運作。
此技能的資料夾為此 SKILL.md 所在的資料夾(以下簡稱 <skill>)。
步驟
1. 收集品牌與文案
詢問對方,或從他們現有的網站上找出:
- 名稱,以及 Logo 的 SVG 檔案(接近正方形;需為純 SVG,不含腳本)。如果沒有,最多三個字母可組成字母組合標誌。
- 顏色:背景色(同時也是地平線的天空顏色,文字顏色必須與它達到 7:1 對比度)與一個強調色。淺藍色會產生正午天空搭配深色文字;深藍色或紫褐色會產生黃昏天空搭配淺色文字與低掛的溫暖太陽。
- 字型:一個用於標題與布料上大字的顯示字型,以及一個用於其他文字的字型。這些會載入固定版本並驗證:Space Grotesk、Inter、Instrument Sans、Instrument Serif、DM Sans、Manrope、Fraunces、Sora、Outfit、Bricolage Grotesque、IBM Plex Sans、JetBrains Mono、Plus Jakarta Sans、Newsreader、Playfair Display。其他 Google Fonts 系列也可使用,但不會固定版本。
- 頁面上的文字:一個包含數個詞的標題(其中一個詞組使用顏色)、一兩句說明用途與目標受眾的句子、主要按鈕與可選的第二個按鈕(含連結)、最多五個導覽連結與導覽列中的按鈕。標題上方的小型徽章與按鈕下方的備註為選填。
- 布料上的文字:頂部印刷的名稱(通常是產品與版本)、下方的一行小字、兩到四個大字(兩行最易閱讀)、最多六個簡短規格配對(標籤與數值)、圖表的標題,以及角落的網址。
每個名稱、數字、宣稱與日期都必須來自對方。不要自行發明規格或數據;寧可省略某個規格。圖表的長條是依據種子繪製的裝飾,並非實際數據:請為它提供一個不宣稱數字的標題,或者將其關閉。
2. 撰寫 hero.json
複製較接近的範例資料夾並取代所有內容:examples/paperwind/(筆記應用、淺色正午天空、四個 peg)或 examples/halden/(亞麻品牌、黃昏天空、三個 peg、較接近正方形的布料)。
| 欄位 | 說明 | 限制 |
|---|---|---|
brand.name、brand.href | 左上角的名稱及其連結(預設為 /) | 28 |
brand.logo | hero.json 旁邊的 .svg 檔案,會內嵌到頁面中;或最多 3 個字母作為字母組合標誌 | – |
nav[] | 最多 5 個 { "label", "href" },顯示在導覽列中(在手機上隱藏) | 18 |
action | 右上角的按鈕 { "label", "href" };設為 false 則不顯示 | 22 |
hero.badge | 標題上方的一行小型大寫文字 | 40 |
hero.title | 標題。*詞組* 會以強調色顯示;\n 可換行(最多 3 行) | 72 |
hero.lede | 說明用途與目標受眾。可使用行內 Markdown:code、連結、粗體、斜體 | 220 |
hero.actions[] | 1 或 2 個 { "label", "href" }:第一個是帶箭頭的實心主要按鈕,第二個是外框按鈕 | 26 |
hero.note | 按鈕下方的一行小字 | 90 |
hero.align | left(繩子與布料在右側,預設值)或 right(鏡像)。center 會將布料放在文案後方;不建議使用 | – |
theme.background、theme.ink、theme.accent | 十六進位顏色。ink 必須與 background 達到 7:1 對比度 | – |
theme.mode | light 或 dark;若未指定,會根據背景色自動判斷 | – |
theme.fonts | { "display", "body", "weight" }:字型系列名稱(設為 false 表示使用系統字型)與標題字重,範圍 300–800(預設 500) | – |
theme.scrim | 0–1:文案後方天空淡化為背景色的強度。預設 0.3 | – |
effect | 天空、繩子與布料,如下所述 | – |
motion.speed | 0.25–2,預設 1:所有動畫的速度 | – |
motion.pointer | true(預設值) | – |
meta | 用於搜尋與連結預覽的 title(70)與 description(170);url 與 image(完整的 https 網址)、lang(預設 en) | – |
印刷內容(effect):至少需要 printTitle 或 printHeadline。
| 設定 | 限制 | 預設值 | 說明 |
|---|---|---|---|
printTitle | 28 個字元 | 無 | 印刷內容頂部的名稱 |
printLine | 64 個字元 | 無 | 名稱下方的一行小字 |
printHeadline | 48 個字元 | 無 | 大字;\n 可換行。文字會自動縮小以符合欄寬 |
printSpecs | 0 到 6 個 { "label", "value" },分別限制 12 與 14 個字元 | 無 | 右側的規格欄;標籤以大寫印刷 |
printChart | true / false | true | 規格下方的小型長條圖,最高長條使用 theme.accent |
printChartLabel | 32 個字元 | 無 | 圖表下方的標題 |
printFoot | 40 個字元 | 無 | 底部角落的小字,例如網址 |
cloth | 顏色 | #ecebe6 | 布料的顏色 |
printInk | 顏色 | #15181d | 印刷文字顏色。必須與 cloth 達到 7:1 對比度 |
場景(effect),所有設定皆為選填:
| 設定 | 範圍 | 預設值 | 作用 |
|---|---|---|---|
seed | 1–9999 | 7 | 決定雲朵與圖表長條的隨機種子 |
skyTop | 顏色 | 淺色為 #5f9be0,深色為背景色加深 | 英雄區塊頂部的天空顏色 |
skyBottom | 顏色 | theme.background | 地平線處的天空顏色 |
clouds | 0–1 | 0.6 | 天空被雲朵覆蓋的比例;0 為晴天 |
cloudColor | 顏色 | 淺色為白色,深色為柔和的紫灰色 | 雲朵的顏色 |
drift | 0–1 | 0.35 | 雲朵飄移的速度 |
light | day、dusk | 淺色為 day,深色為 dusk | 正午太陽從上方照射,或低掛的溫暖太陽與冷色天空光 |
aspect | 0.8–1.7 | 1.35 | 布料的寬高比 |
pegs | 2–6 | 4 | 頂部邊緣的木夾數量 |
peg | 顏色 | #c39a64 | 木夾的顏色 |
rope | 顏色 | 淺色為 #6f6a62,深色為 #cfc6b8 | 繩子的顏色 |
breeze | 0–1 | 0.45 | 微風吹起布料的強度 |
weave | 0–1 | 0.5 | 近距離下平紋織法的可見度 |
poke | 0–1 | 1 | 游標下凹陷的深度;設為 0 可關閉 |
drag | true / false | true | 允許訪客抓取並拉動布料 |
size | 0.6–1 | 0.76 | 布料佔據文案旁空間的比例(需預留拉動空間) |
hint | 40 個字元 | "Press it · pull it · let go" | 寬螢幕畫面中右下角的提示文字,告訴訪客可以觸摸;" · " 分隔各部分;留空則不顯示 |
撰寫提示:頁面上的標題與布料上的大字應傳達不同內容;布料看起來像一張海報,因此文字應簡潔且大。兩到三個規格配對比六個更容易閱讀。如果品牌有與輕盈、飛行、晾乾或懸掛相關的俏皮標語,布料是放置它的好地方。
3. 建置
node <skill>/scripts/build.mjs hero.json out
它會一次性列出 hero.json 中的所有問題(缺少欄位、文字超過限制、設定超出範圍、顏色對比不足、布料上印刷顏色太淡等),並在修正前不會寫入任何檔案。接著它會列印所使用的設定以及需要注意的事項,例如為了讓標題達到 4.5:1 對比度而加深的強調色。
它會輸出 out/index.html(內嵌 CSS、腳本、Logo 與靜態圖片)、out/embed.html 與 out/assets/logo.svg(分頁圖示)。
4. 檢查
node <skill>/scripts/check.mjs out/index.html
它會在 390、768、1024 與 1440 像素寬度下載入頁面,並在各個寬度下顯示 ok:沒有任何元素超出畫面或被裁切、每行文字與背景天空的對比度皆可閱讀、每個連結與按鈕都有名稱、一個 h1。標示 · 的行需要檢查但不會導致檢查失敗。
5. 觀看並觸摸
node <skill>/scripts/record.mjs out/index.html --still 1.6 --size 1440x900
node <skill>/scripts/record.mjs out/index.html --still 3.5 --size 1440x900
node <skill>/scripts/record.mjs out/index.html --still 1.6 --size 390x844 --scale 2
node <skill>/scripts/screenshot.mjs out/index.html
第一張是布料靜止狀態,第二張是拉動中(腳本控制的手拉起一角),第三張是手機畫面。檢查它們:布料是否完整顯示且周圍有適當空間、印刷文字是否清晰可讀、木夾是否正確掛在繩上?screenshot.mjs 會在沒有 GPU 的情況下執行 Chrome,因此它會顯示訪客在沒有 WebGL 或網路失敗時看到的靜態圖片(位於 out/screens/ 中);檢查它是否也清晰可讀。
然後在瀏覽器中打開 out/index.html 並試玩:懸停、按壓、將一角拉過繩子、放手。在手機上用手指拖曳布料,並確認在布料旁邊滑動時頁面仍可正常捲動。
6. 錄製影片
node <skill>/scripts/record.mjs out/index.html --seconds 10
它會以 30 fps、1920×1080 解析度逐步驅動英雄區塊 10 秒,並輸出 out/preview.mp4 與 out/poster.png(3.5 秒處的幀,布料正被拉起)。在影片中,一個繪製的游標會在 2.2 秒時抓住布料下角、朝你拉起、在 4.4 秒時鬆開,然後從 6 秒開始用指尖橫掃布料。選項:--size 1080x1350(直向)、--size 1080x1920、--fps 60、--poster 1.6。只要錄製從 0 開始(它總是如此),所有機器的幀都相同。
7. 上線
- 作為頁面:將
out/index.html與out/assets/上傳至任何靜態主機(Cloudflare Pages、Netlify、Vercel、GitHub Pages、S3)。使用out/poster.png(或它的 1200×630 裁切版本)作為meta.image,並與網站一起上傳。 - 嵌入現有網站:將
out/embed.html貼到英雄區塊應出現的位置。所有內容都限定在#mh-washing-line範圍內;如果你願意,可以將字型<link>移到<head>中。如果網站已有<script type="importmap">,請將嵌入的imports與integrity項目合併進去。此區塊預設為全視窗高度;可在它上面設定--mh-height(例如style="--mh-height:760px")來縮小高度。在 React 或其他框架中,將標記放在元件內,並將<script>標籤放在頁面中,或使用 iframe 渲染。 - 若要更改印刷內容,請編輯
hero.json並重新建置。
疑難排解
build: fix hero.json first:每行都會指出需要修改的欄位與修改方式;顏色問題會建議一個可用的顏色。- 布料在邊緣被裁切:降低
size,或讓布料更接近正方形(aspect接近 1)。 - 布料上的大字太小:它們會自動縮小以符合規格欄的寬度。使用較少的文字、加入
\n,或減少規格與圖表(文字會佔滿整個寬度)。 - 布料無法靜止,或晃動過大:降低
breeze。 - 凹陷太強或太弱:調整
poke(設為 0 可關閉凹陷;拖曳功能仍可運作)。 record: the effect didn't start:頁面已降級為靜態圖片:Chrome 無法從 jsDelivr 載入 three.js(請檢查網路連線或代理設定),或沒有 WebGL。ffmpeg not found:請安裝 ffmpeg 或設定FFMPEG_PATH。- 訪客看到靜態布料:他們的瀏覽器關閉了 WebGL,或 CDN 被封鎖(顯示平面圖片),或者他們要求減少動畫(顯示一幀靜止的即時布料,不會移動)。這是設計使然。
- 在手機上頁面無法捲動:它只會在一根手指按住布料時停止捲動;在布料旁邊滑動即可。如果布料填滿手機螢幕,請降低
size。
檔案
scripts/build.mjs、scripts/record.mjs:你需要執行的腳本。它們會呼叫 Motion Heroes 套件。scripts/kit/:所有 Motion Heroes 技能共用的套件:建置與檢查(build.mjs)、區塊的 CSS(shell.css)、瀏覽器執行時期(runtime.js:時鐘、暫停、像素比、減少動畫、備援)、錄製器(record.mjs、cdp.mjs)、固定的 three.js 與字型(three.json、fonts.json)以及KIT.md(說明文件)。scripts/effect/:曬衣繩本身:effect.mjs(設定、檢查與靜態圖片)、effect.js(天空著色器、布料模擬與渲染)、composition.md(說明場景編排的文件)、record.mjs(指定錄製步驟)、screenshot.mjs(靜態截圖)、wasm/(模擬引擎的 WebAssembly 二進位檔案)、font.css(字型載入)。