MotionCraft 建構器 是做什麼的?
這個技能教導代理程式以小型工作室交付獲獎作品的方式來建立動態網站:一個想法、一份書面計畫、一個以灰階運作的機制、然後進行裝飾,接著是嚴格的審視與修正。工藝的精髓存在於參考資料中。這個檔案就是工作流程。
預設技術棧: 語意化 HTML、CSS、Vanilla JavaScript 模組、透過 import map 使用的 three.js 0.170、無建置步驟。僅在配方需要時才加入 GSAP 或 Lenis。每個頁面都必須在 WebGL 關閉的狀態下能正常運作、閱讀並轉換。
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/"
}
}
</script>
工作流程
請依序執行這些步驟。在每個步驟產出可展示的成果之前,不要跳過直接進入特效環節。
第一步:找到核心概念
閱讀需求簡報,並列出客戶的真實世界:他們處理的物件、他們列印的文件、場所的聲音、員工整天使用的動詞。然後寫下一句話:
[品牌] 是一個網站,你在其中 [動詞] [他們世界中的物件],它會向你展示 [他們販售的價值]。
使用虛構品牌的範例:
- Cinderford Coffee Roasters:你滾動一顆咖啡豆通過烘焙過程,從綠色到第一次爆裂再到 full city,當顏色變化時,風味筆記會列印在烘焙記錄上。
- Lakemont Bike Fitters:你拖曳騎士的坐墊高度,膝蓋角度弧線會即時在側面視圖上重新繪製。
- Halvorsen Tide Charters:你滑動潮汐時鐘,船隻會隨著真實碼頭的情況上升和下降。
關於核心概念的規則(參閱 工藝規則 1 至 3):
- 一個物件、一個動詞、一個世界。如果你需要用到兩次「和」,那就是兩個概念了。選一個。
- 隱喻來自客戶的世界,絕非來自流行趨勢。車庫用維修單說話、雜貨商用每週廣告、交通運輸業用路線圖。
- 賦予強調色單一任務。如果紅色代表「發現錯誤」,那麼它在頁面上的任何其他地方都不得有其他含義。
- 載入器、游標、導航、懸停狀態、過場動畫和 404 頁面都使用相同的語言。
產出: 這句話、動詞、強調色及其含義,以及三個被否決的概念,每個附上一行說明為什麼被否決。
第二步:選擇頁面架構
開啟 page-architecture.md。選擇一個來自於與核心概念相同世界的導航原型、英雄區塊構成和區段順序。切勿重複使用上一個專案的骨架。將這個選擇記錄在專案 README 中,以便下一個建構可以避免使用它。
產出: 一張簡短的架構卡(導航、英雄區塊、區段列表、頁尾、404),包含你使用的命名模式。
第三步:撰寫節拍表
在撰寫任何程式碼之前,先寫好腳本。使用 beat-sheet.md 中的範本。每個節拍應說明其滾動範圍、訪客所見、移動的元素、文案、攝影機、手機畫面構圖、減少動畫版本以及成本。範例展示了預期的詳細程度。
產出: 填寫完成的節拍表。之後的每個決策都將與其比對。如果某個區段無法服務於第一步驟中的那句話,就在這裡將它刪除,因為此時修正的成本最低。
第四步:以灰階模型驗證機制
使用佔位用的幾何體和佔位用的文案來建構動態效果。這個步驟在任何資產存在之前,先驗證了時間、攝影機和互動的可行性。
- 首先撰寫 DOM:真實的標題、真實的文案區塊、真實的按鈕和表單,每個節拍一個
<section class="shot">。頁面必須在指令碼關閉的狀態下仍能由上到下閱讀。 - 在 DOM 背後添加一個固定的 canvas 和一個渲染循環。滾動、指標、音訊和渲染共享同一個循環(規則 8)。
- 將滾動映射到一個計算節拍數量的單一浮點數
t,並透過帶有 smoothstep 停留的鏡頭列表來移動攝影機(參閱滾動與攝影機配方)。 - 對每個物體使用灰色方塊、球體和平面。如果故事在灰色狀態下無法成立,那麼裝飾也無法挽救它。
- 現在就加入暫停控制和減少動畫的分支,而不是在最後才做(參閱互動與無障礙配方)。
灰階模型起始點。頁面需要上述的 import map、一個固定尺寸的 canvas,以及 SHOTS 陣列中每個條目對應的一個 .shot 區段:
<style>
#gl { position: fixed; inset: 0; width: 100%; height: 100%; z-index: -1; display: block; }
.shot { min-height: 100svh; display: grid; align-items: center; padding: 16px 6vw; }
.no-gl #gl { display: none; }
</style>
<canvas id="gl" aria-hidden="true"></canvas>
<main>
<section class="shot" id="top"><h1>來自節拍 0 的標題</h1></section>
<section class="shot" id="detail"><h2>節拍 1</h2></section>
<section class="shot" id="side"><h2>節拍 2</h2></section>
</main>
<script type="module" src="main.js"></script>
main.js:
import * as THREE from "three";
const canvas = document.getElementById("gl");
let renderer = null;
try { renderer = new THREE.WebGLRenderer({ canvas, antialias: true, powerPreference: "high-performance" }); }
catch { document.body.classList.add("no-gl"); } // DOM 頁面本身就能獨立運作
if (renderer) {
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111114);
scene.add(new THREE.HemisphereLight(0xffffff, 0x222233, 1.4));
const camera = new THREE.PerspectiveCamera(35, 1, 0.1, 100);
// 在節拍表簽核之前,作為英雄物體的灰色替代
scene.add(new THREE.Mesh(new THREE.BoxGeometry(2, 1, 1.2), new THREE.MeshStandardMaterial({ color: 0x8a8a8a, roughness: 0.6 })));
const V = (x, y, z) => new THREE.Vector3(x, y, z);
const SHOTS = [
{ pos: V(4, 3, 8), at: V(-1.5, 0, 0) }, // 英雄區塊:物體在標題右方
{ pos: V(0, 9, 0.1), at: V(0, 0, 0) }, // 俯視細節
{ pos: V(-6, 1.2, 3), at: V(1.5, 0, 0) }, // 低角度側面,文案在右側
];
const sections = [...document.querySelectorAll(".shot")];
let centers = [];
const measure = () => { centers = sections.map((s) => s.offsetTop + s.offsetHeight / 2); };
const smooth = (a, b, x) => { const k = Math.min(1, Math.max(0, (x - a) / (b - a))); return k * k * (3 - 2 * k); };
function scrollT() {
const c = scrollY + innerHeight / 2;
if (!centers.length || c <= centers[0]) return 0;
for (let i = 0; i < centers.length - 1; i++) if (c < centers[i + 1]) return i + (c - centers[i]) / (centers[i + 1] - centers[i]);
return centers.length - 1;
}
function resize() {
renderer.setPixelRatio(Math.min(devicePixelRatio, innerWidth < 760 ? 1.5 : 1.75));
renderer.setSize(innerWidth, innerHeight, false);
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
measure();
}
addEventListener("resize", resize);
document.fonts?.ready.then(measure);
resize();
const pos = new THREE.Vector3(), at = new THREE.Vector3();
let raf = 0;
function frame() {
raf = requestAnimationFrame(frame);
const t = scrollT(), i0 = Math.floor(t), i1 = Math.min(SHOTS.length - 1, i0 + 1);
const f = smooth(0.15, 0.85, t - i0); // 在每個鏡頭處停留,在中間區域移動
pos.lerpVectors(SHOTS[i0].pos, SHOTS[i1].pos, f);
at.lerpVectors(SHOTS[i0].at, SHOTS[i1].at, f);
camera.position.copy(pos);
camera.lookAt(at);
renderer.render(scene, camera);
}
raf = requestAnimationFrame(frame);
document.addEventListener("visibilitychange", () => {
cancelAnimationFrame(raf); // 絕不運行兩個循環
if (!document.hidden) raf = requestAnimationFrame(frame);
});
}
第四步的退出標準: 向前和向後滾動遍歷每個節拍,攝影機在兩種方向上都以相同的方式到達定位;每個鏡頭都為其文案預留了空間;手機畫面構圖在 390 x 844 解析度下能正常顯示;暫停控制能停止所有連續運動;頁面在 WebGL 停用時仍可閱讀。
第五步:裝飾它
現在用真實世界替換灰色。從技術索引中提取配方。按以下順序進行裝飾,並在每一層之後與節拍表進行比對:
- 形體: 英雄物體,由基本幾何體、glTF、影片或深度照片構成。
- 光線: 主光、邊光和一個帶有動機的強調光。任何放在平面上的物體下方都要有接觸陰影。
- 材質與紋理: 在照片紋理之前,優先使用 canvas 繪製的標籤和麵板。它們銳利、微小且可編輯。
- 字體: 最多使用兩種字體。標題動畫需與頁面節奏相關聯,絕不使用泛用的淡入效果。
- 音效: 可選,需主動啟用,使用 Web Audio 合成以節省頻寬。
- 小獎勵: 一個隱藏的手勢、一個閒置狀態、一個使用品牌語言的 404 頁面。
將影格時間花在互動上,而不是浪費在沒人注意的物理效果上。偽造成本高昂的效果(規則 17)。
第六步:審視與修正
在你親眼審視之前,你尚未完成。將頁面交給 motion-review 技能(../motion-review/SKILL.md)進行評分標準、QA 檢查清單以及桌面和手機尺寸的區塊截圖審查,並交給 motion-analytics 技能(../motion-analytics/SKILL.md)進行影格時間、權重和參考對比評分。然後,優先修正最嚴重的問題,然後再次審視。對於任何你稱之為「完成」的作品,至少需要進行兩次完整的審視與修正循環。
在正式審查前進行的快速自我檢查:
- 向下滾動,然後一路滾回頂端。每個揭露動畫都必須乾淨地反向執行。
- 在動畫進行中按下暫停控制。所有連續運動都必須停止,並且頁面看起來仍須完整。
- 在作業系統中開啟減少動畫模式。頁面必須以靜態畫面講述相同的故事。
- 打開主控台。沒有你所導致的錯誤或警告。
- 手機寬度 390 px:沒有小於 11 px 的文字,沒有小於 44 px 的觸控目標,沒有水平滾動。
第七步:交付
交付頁面資料夾(index.html、style.css、main.js 及其模組、assets/),以及一份 README,其中包含核心概念的一句話、架構卡、節拍表、附有授權資訊的資產列表,以及值得調整的調校值。保持整個頁面在 10 MB 以下。
不可妥協事項
- 底層是真實的 HTML。內容、導航和表單都位於 DOM 中。WebGL 是一個增強功能,並帶有
no-gl的備援方案。 - 單一時鐘。絕不將逐影格的值放入框架狀態中。使用普通變數、uniforms 和 CSS 自訂屬性。
- 對於任何運行時間超過 5 秒的動畫,必須有一個可見的暫停控制項,以及一個完整的
prefers-reduced-motion分支。 - 絕不劫持滾動。原生滾動條必須能運作,鍵盤滾動必須能運作,且錨點連結必須能跳轉。
- 在示範中使用虛構品牌和虛構人物。絕不從參考網站複製程式碼、文案、資產或英雄概念。研究其概念,然後自己撰寫。
- 頁面上的每個數字(價格、時間、統計數據)都必須是合理的、一致的,並符合客戶的在地設定。
參考資料對照表
| 檔案 | 何時使用 |
|---|---|
| references/craft-rules.md | 做出任何品味判斷時。30 條帶有具體數值的編號規則。 |
| references/beat-sheet.md | 規劃故事時。包含範本和一個完整的範例。 |
| references/page-architecture.md | 選擇導航、英雄和區段,避免模板千篇一律。 |
| references/techniques/INDEX.md | 根據需求、成本或亮點來查找配方。 |
| techniques/scroll-and-camera.md | 滾動映射、攝影機鏡頭、手機畫面構圖、模式數值。 |
| techniques/scene-craft.md | three.js 裝飾:標註、變形、城市、天空、標籤、陰影。 |
| techniques/film-and-photo.md | 影片英雄、循環、配合影片時機的文案、追蹤熱點、深度照片。 |
| techniques/sound-and-rhythm.md | Web Audio 語音、排程、與節拍同步的視覺效果。 |
| techniques/interaction-and-access.md | 暫停控制、錨點、保持閘門、拖曳視圖、品質分級。 |
屬於 RegesCore AI MotionCraft Skills,由 LIKE A KING INC. 提供。免費動態範本和完整目錄請造訪 animatedwebs.com。