MotionCraft Builder 是做什么的?
该技能教导 AI 代理像精品工作室交付获奖作品一样构建动效网站:一个想法、一份书面计划、一个灰盒中的工作机制、然后进行装饰,最后严格审查并修复。工艺精髓在于参考资料。本文件即是工作流程。
默认技术栈: 语义 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>
工作流程
按顺序执行以下步骤。在展示给客户之前,不要跳到效果实现。每个步骤都有可以展示的产出。
步骤 1. 找到单一概念
阅读简报,列出客户的真实世界:他们处理的物体、打印的文件、场所的声音、员工整天使用的动词。然后写一句话:
[品牌] 是一个网站,你在其中 [动词] 来自他们世界的 [物体],它会向你展示 [他们销售的价值]。
虚构品牌示例:
- Cinderford Coffee Roasters:你滚动一颗咖啡豆经历烘焙过程,从绿色到一爆再到全城,随着颜色变化,风味笔记会打印在烘焙日志上。
- Lakemont Bike Fitters:你拖动骑手的坐垫高度,膝盖角度弧线会在实时侧视图中重新绘制。
- Halvorsen Tide Charters:你滑动潮汐时钟,船只会在真实码头前上下浮动。
概念规则(参见工艺规则 1 至 3):
- 一个物体,一个动词,一个世界。如果你需要两次“并且”,那就是两个概念,选其中一个。
- 隐喻必须来自客户的世界,而非来自潮流。车库用维修单说话,杂货商用每周传单说话,交通机构用线路图说话。
- 给强调色一个任务。如果红色表示“发现故障”,那么它在页面任何其他位置都别无含义。
- 加载器、光标、导航、悬停状态、过渡效果和 404 页面都必须使用同一种语言。
产出: 该句话、动词、强调色及其含义,以及三个被否决的概念(每个附带一行落选原因)。
步骤 2. 选择页面架构
打开 page-architecture.md。选择与概念同源的导航原型、首屏构成和区块序列。切勿重用前一个项目的骨架。将选择记录在项目 README 中,以便后续构建避免重复。
产出: 一个简短的架构卡片(导航、首屏、区块列表、页脚、404),注明所使用的命名模式。
步骤 3. 编写节奏表
在任何代码之前编写剧本。使用 beat-sheet.md 中的模板。每个节拍需说明其滚动范围、访客所见内容、移动元素、文案、摄像机、手机构图、减少动效版本及成本。示例展示了期望的详细程度。
产出: 填写完整的节奏表。后续所有决策都以此为准绳。如果某个区块不为步骤 1 中的那句话服务,在此处删除它——此刻删除成本最低。
步骤 4. 灰盒化机制
使用占位几何体和占位文案构建动效。此步骤在任何素材存在之前验证时序、相机和交互。
- 优先编写 DOM:真实的标题、真实的文案块、真实的按钮和表单,每个节拍对应一个
<section class="shot">。页面必须在脚本关闭的情况下从上到下可读。 - 在 DOM 后方添加一个固定画布和一个渲染循环。滚动、指针、音频和渲染共享同一个循环(规则 8)。
- 将滚动映射为一个单一的浮点数
t,用于计数节拍,并通过一个带有平滑插值的镜头列表移动摄像机(滚动与相机配方)。 - 所有对象均使用灰盒子、球体和平面。如果故事在灰盒状态下无法正常工作,装饰也无法挽救。
- 立即添加暂停控制和减少动效分支,而不是在结束时(交互配方)。
灰盒起始模板。页面需要上述 import map、一个固定画布以及每个 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);
});
}
步骤 4 退出标准: 向前和向后滚动经过每个节拍,摄像机在两个方向上应一致对齐;每个镜头都为其文案留有空间;手机构图在 390 x 844 分辨率下工作正常;暂停控制停止所有持续运动;关闭 WebGL 后页面可读。
步骤 5. 装饰
现在用真实世界替换灰色。从技术索引中提取配方。按以下顺序进行装饰,每层完成后对比节奏表进行检查:
- 形态: 首屏对象,由基本几何体、glTF、影片或深度照片构建。
- 光照: 主光、轮廓光和一束动机明确的强调光。任何放置在表面上的物体下方都需要接触阴影。
- 材质与纹理: 在照片纹理之前优先使用 Canvas 绘制的标签和面板。它们清晰、微小且可编辑。
- 字体: 最多两种字体。标题动效应与页面节奏相关联,避免使用通用的淡入淡出效果。
- 音效: 可选、需用户选择加入、使用 Web Audio 合成以节省字节。
- 小彩蛋: 一个隐藏手势、一个空闲状态、一个使用品牌语言的 404 页面。
将帧时间花在交互上,而不是无人注意的物理效果上。模拟昂贵的部分(规则 17)。
步骤 6. 检查并修复
在审查之前,你尚未完成。将页面交给 motion-review 技能(../motion-review/SKILL.md)进行评分、QA 检查清单以及桌面和手机尺寸的截图;同时交给 motion-analytics 技能(../motion-analytics/SKILL.md)进行帧时间、页面权重和参考对比评分。然后修复最先发现的最严重的错误,并再次审查。对于任何你称之为“完成”的作品,至少需要进行两次完整的检查与修复循环。
正式审查前的快速自查:
- 向下滚动,然后一路滚回顶部。每个显现效果必须能干净地反向。
- 在动效过程中按下暂停控制。所有持续运动必须停止,且页面看起来仍然完整。
- 在操作系统中开启“减少动效”。页面必须使用静态画面讲述相同的故事。
- 打开控制台。零错误,零由你造成的警告。
- 手机宽度 390 px:无小于 11 px 的文字,无小于 44 px 的点击目标,无水平滚动。
步骤 7. 交付
交付页面文件夹(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。