3D 前端 — 滚动驱动型3D网站
创建沉浸式单文件HTML页面,用户通过滚动在由Three.js构建的3D场景中漫步。内容(标题、文本、图片、行动号召按钮)叠加或嵌入在3D世界中。每个DreamWalk页面都是一个自包含的.html产物——无需构建工具、无需npm、无需React。
技术栈
所有输出均为单文件HTML产物。无需构建工具、无需npm、无需React——仅需一个自包含的.html文件。
| 库 | CDN | 用途 |
|---|---|---|
| Three.js r128 | https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js | 3D渲染引擎 |
| GSAP 3.12.5 | https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js | 动画引擎 |
| ScrollTrigger 3.12.5 | https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js | 滚动到动画的绑定 |
为什么选择这个技术栈? Three.js 是主流 WebGL 框架,拥有最庞大的生态系统。GSAP ScrollTrigger 是将滚动位置映射到动画时间线的行业标准。它们共同驱动了大多数获奖的3D滚动网站(参见 Awwwards、FWA)。这种组合可以在单个 HTML 文件中工作,无需构建步骤,非常适合产物制作。
重要提示:Three.js r128 API 限制。 这不是最新的 Three.js。以下 API 在 r128 中不存在,绝不能使用:
CapsuleGeometry、RoundedBoxGeometry、THREE.MathUtils.seededRandom、WebGPURenderer、THREE.ColorManagement。始终使用BufferGeometry基础图元(BoxBufferGeometry、SphereBufferGeometry等)或其别名(BoxGeometry、SphereGeometry)。
架构模式
┌─────────────────────────────────────────┐
│ HTML 页面 │
│ ┌───────────────────────────────────┐ │
│ │ <canvas> - Three.js 场景 (固定) │ │
│ │ • 摄像机沿路径/样条运动 │ │
│ │ • 3D物体 (几何体/粒子) │ │
│ │ • 光照与雾效 │ │
│ └───────────────────────────────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ <div class="scroll-container"> │ │
│ │ height: 500vh+ (可滚动) │ │
│ │ 包含文本/内容的章节 │ │
│ │ 通过 position:fixed 或 │ │
│ │ 在滚动触发时透明度渐变进行叠加 │ │
│ └───────────────────────────────────┘ │
│ │
│ GSAP ScrollTrigger 映射 滚动百分比 → │
│ camera.position / camera.lookAt / │
│ object transforms / material params │
└─────────────────────────────────────────┘
如何构建3D滚动页面
在编写代码之前,请阅读 references/PATTERNS.md 以获取详细、可直接复制的代码模式。 其中包含40多个生产就绪的模式,涵盖摄像机动画、光照、着色器、粒子、程序化几何体和性能优化——全部兼容 Three.js r128。
步骤 1:场景设计
根据用户意图决定体验类型:
| 类型 | 摄像机行为 | 最佳用途 |
|---|---|---|
| 隧道 / 走廊 | 沿 Z 轴向前飞行 | 产品展示、故事叙述 |
| 轨道 / 展示 | 围绕中心物体旋转 | 产品页面、作品集 |
| 地形飞越 | 扫过地形/城市景观 | 落地页、介绍页 |
| 房间漫游 | 在连接的空间中移动 | 虚拟导览、演示文稿 |
| 垂直下降 | 摄像机沿 Y 轴下降 | 时间线、图层揭示 |
| 样条路径 | 摄像机沿弯曲的3D路径运动 | 电影感、编辑类内容 |
步骤 2:构建 HTML 骨架
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D 体验</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { overflow-x: hidden; }
canvas { position: fixed; top: 0; left: 0; z-index: 0; }
.scroll-container { position: relative; z-index: 1; pointer-events: none; }
.section { height: 100vh; display: flex; align-items: center; justify-content: center; }
.content { pointer-events: auto; /* 为交互元素重新启用 */ }
</style>
</head>
<body>
<!-- Three.js 画布通过程序创建 -->
<div class="scroll-container">
<!-- 章节驱动滚动高度;内容叠加在 3D 场景之上 -->
<div class="section" id="section-1">...</div>
<div class="section" id="section-2">...</div>
<!-- 增加更多章节 = 更长的滚动距离 = 更长的摄像机旅程 -->
</div>
<script src="THREE_CDN"></script>
<script src="GSAP_CDN"></script>
<script src="SCROLLTRIGGER_CDN"></script>
<script>
// 场景设置、动画、ScrollTrigger 绑定
</script>
</body>
</html>
步骤 3:核心 Three.js 设置
始终包含以下基础要素:
- 场景 + 摄像机 + 渲染器 — 使用
PerspectiveCamera,FOV 为 60-75 以获得沉浸感 - 光照 — 至少:一个
AmbientLight+ 一个DirectionalLight。为增加戏剧效果可添加PointLight或SpotLight - 雾效 —
scene.fog = new THREE.FogExp2(color, density)增加深度感并隐藏新出现的物体 - 尺寸调整处理 — 始终处理
window.resize - 渲染循环 —
requestAnimationFrame配合renderer.render(scene, camera)
步骤 4:使用 GSAP 实现滚动 → 摄像机动画
关键的绑定模式:
gsap.registerPlugin(ScrollTrigger);
// 将摄像机动画目标存储为普通对象
const camAnim = { x: 0, y: 2, z: 10, lookX: 0, lookY: 0, lookZ: 0 };
const tl = gsap.timeline({
scrollTrigger: {
trigger: ".scroll-container",
start: "top top",
end: "bottom bottom",
scrub: 1, // 1秒平滑过渡
}
});
// 链式摄像机关键帧
tl.to(camAnim, { x: 0, y: 2, z: 5, lookX: 0, lookY: 0, lookZ: 0, duration: 1 })
.to(camAnim, { x: 3, y: 1, z: 0, lookX: 2, lookY: 0, lookZ: -5, duration: 1 })
.to(camAnim, { x: 0, y: 5, z: -10, lookX: 0, lookY: 0, lookZ: -15, duration: 1 });
// 在渲染循环中应用动画值
function animate() {
requestAnimationFrame(animate);
camera.position.set(camAnim.x, camAnim.y, camAnim.z);
camera.lookAt(camAnim.lookX, camAnim.lookY, camAnim.lookZ);
renderer.render(scene, camera);
}
步骤 5:内容覆盖层
将 HTML 内容叠加,并在滚动位置淡入/淡出:
// 当摄像机到达一个路点时淡入一个章节
gsap.fromTo("#section-2 .content",
{ opacity: 0, y: 50 },
{ opacity: 1, y: 0,
scrollTrigger: {
trigger: "#section-2",
start: "top center",
end: "top 20%",
scrub: true
}
}
);
步骤 6:视觉润色
应用以下技巧,将基础页面提升为令人印象深刻的作品:
- 粒子系统 — 使用
THREE.Points配合THREE.BufferGeometry制作漂浮的尘埃/星星 - 后期处理发光 — 使用自发光材质和类泛光效果的亮色
- 颜色过渡 — 随滚动动画改变
scene.background和scene.fog.color - 物体揭示 — 摄像机靠近时,物体从 0 缩放或淡入
- 视差层 — 不同距离的物体以不同速度移动
- 微旋转 — 在渲染循环中缓慢旋转装饰性物体(独立于滚动)
- 鼠标视差 — 根据鼠标位置稍微偏移摄像机以增加深度感
设计原则
- 性能优先 — 保持几何体简单。使用
BufferGeometry,限制绘制调用,使用fog隐藏远处物体。目标 60fps - 平滑滚动 — 在 ScrollTrigger 中使用
scrub: 1或scrub: 2实现丝滑运动。绝不使用scrub: true(即时而卡顿) - 内容可读性 — 文字叠加需要对比度。在亮色文字后使用半透明深色面板,或确保3D背景在文字区域后是深色/模糊的
- 移动端降级 — 触摸滚动可与 ScrollTrigger 配合使用。考虑通过
window.innerWidth检查在移动端减少粒子数量和几何复杂度 - 优雅加载 — 显示一个简单的 CSS 加载画面,当 Three.js 场景准备就绪时隐藏它
常见的可构建 3D 物体(无需外部模型)
所有这些都可以使用 Three.js 内置几何体创建——无需 GLTF 文件:
- 漂浮的几何体 — BoxGeometry、IcosahedronGeometry、TorusKnotGeometry 搭配 MeshStandardMaterial
- 地形 — 带有顶点位移(正弦波、噪声)的 PlaneGeometry
- 粒子场 — BufferGeometry + Points 用于星星、尘埃、雪、数据可视化
- 网格地板 — GridHelper 或自定义线几何体
- 管状/隧道 — 沿 CatmullRomCurve3 路径的 TubeGeometry
- 文字 — 渲染到 PlaneGeometry 的画布纹理,或基于精灵的文字
- 线框物体 — 任何带有
wireframe: true材质的几何体,用于科技美学 - 光束 — 使用自发光透明材质的 CylinderGeometry
步骤 7:鼠标视差(可选但推荐)
根据鼠标移动增加轻微的摄像机偏移以感知深度:
const mouse = { x: 0, y: 0 };
document.addEventListener('mousemove', (e) => {
mouse.x = (e.clientX / window.innerWidth - 0.5) * 2; // -1 到 1
mouse.y = (e.clientY / window.innerHeight - 0.5) * 2;
});
// 在渲染循环中,为摄像机位置添加微小的偏移
const parallaxStrength = 0.3;
camera.position.x = camAnim.x + mouse.x * parallaxStrength;
camera.position.y = camAnim.y - mouse.y * parallaxStrength * 0.5;
房间漫游模式
对于建筑/房地产漫游,使用简单几何体构建房间:
// 使用 BoxGeometry 创建墙壁、地板、天花板,组成一个房间
function createRoom(width, height, depth, position, color) {
const mat = new THREE.MeshStandardMaterial({ color, side: THREE.BackSide });
const room = new THREE.Mesh(new THREE.BoxGeometry(width, height, depth), mat);
room.position.copy(position);
scene.add(room);
return room;
}
// 使用简单几何体创建家具占位符
// 沙发 = 宽扁盒子,桌子 = 圆柱上的薄盒子,灯 = 圆柱 + 球体
// 在房间内使用自发光 PointLight 营造温暖的室内氛围
房间漫游的关键原则:
- 在材质上使用
THREE.BackSide,使墙壁从内部渲染可见 - 在每个房间内放置暖色
PointLight光源(颜色0xffeedd,强度 0.8) - 摄像机路径应从门洞进入,而不是穿过墙壁
- 摄像机进入时,动画
lookAt自然地扫描每个房间 - 添加带有自发光明亮材质的矩形窗户,模拟日光
应避免的反模式
- ❌ 不要使用
OrbitControls——滚动应是唯一的导航方式 - ❌ 不要加载外部 GLTF/GLB 模型——保持内容自包含在 HTML 文件中
- ❌ 不要使用
THREE.CapsuleGeometry——r128 中不可用 - ❌ 不要全局设置 canvas
pointer-events: none——渲染循环需要画布 - ❌ 不要忘记
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))——防止 Retina 屏幕上的 GPU 过载 - ❌ 不要直接使用
camera.rotation动画摄像机——使用camera.lookAt()配合动画后的目标坐标以获得稳定结果 - ❌ 不要让滚动容器太短——最少需要 400vh 才能有一次有意义的旅程,600vh+ 更佳
美学方向
应用来自 frontend-design 技能的创造性胆识。每个 3D 滚动页面都应感觉独特:
- 选择一种色彩情绪:霓虹赛博朋克、温暖日落、深海、冰晶、带有自发光点缀的黑暗虚空等
- 选择一种几何语言:有机曲线 vs 硬边、稀疏 vs 密集、大型巨石 vs 微小粒子
- 排版叠加:导入独特的 Google Fonts。使用大尺寸、电影感的字体。在滚动时动画化不透明度和变换
- 3D 场景和 HTML 内容应感觉像一个 cohesive 的整体体验,而不是一个贴了文字的 3D 背景
快速参考:CDN URL
Three.js r128: https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js
GSAP 3.12.5: https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js
ScrollTrigger: https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js