Three.js 开发实战教程(一):环境搭建与第一个 3D 场景
本专栏将围绕 Three.js 核心能力,从基础到进阶,通过实战案例带大家掌握 3D 可视化开发。第一篇聚焦「入门第一关」—— 解决环境搭建问题,并实现第一个可交互的 3D 场景,为后续学习打下基础。
一、Three.js 是什么?
Three.js 是基于 WebGL 的 JavaScript 3D 库,它封装了 WebGL 的复杂底层 API,让开发者无需深入图形学原理,就能快速创建 3D 场景、模型与动画。其核心应用场景包括:
- 3D 产品展示(电商商品预览、工业设备拆解)
- 数据可视化(GIS 地理信息、大屏数据 3D 呈现)
- 轻量游戏开发(Web 端小游戏、互动场景)
- 虚拟仿真(教育课件、医疗模拟演示)
二、开发环境搭建(Vue3 + Vite + Three.js)
本专栏统一采用「Vue3 + Vite」技术栈(前端主流组合,打包速度快、开发体验佳),以下是完整搭建步骤:
1. 创建 Vite 项目
确保已安装 Node.js(建议 v16+),打开终端执行以下命令:
# 创建项目(项目名自定义,示例为threejs-demo)
npm create vite@latest threejs-demo -- --template vue# 进入项目目录
cd threejs-demo# 安装项目依赖
npm install# 启动开发服务(默认端口5173,可在vite.config.js中修改)
npm run dev
2. 安装 Three.js 相关依赖
需安装 Three.js 核心包与类型定义文件(非 TS 项目安装类型定义,可获得更好的 IDE 语法提示):
npm install three @types/three
3. 项目核心目录梳理
后续实战案例将重点关注src/components目录,核心结构如下:
threejs-demo/
├─ src/
│ ├─ components/ # 存放3D相关组件(重点目录)
│ ├─ App.vue # 项目入口组件
│ └─ main.js # 项目入口文件(初始化Vue实例)
└─ package.json # 项目依赖与脚本配置
三、实战:创建第一个 3D 场景(旋转立方体)
本次实战将实现 Three.js 最基础的「三要素 + 物体 + 动画」结构,最终呈现「彩色立方体绕轴旋转」的效果,帮助理解 3D 场景的核心构成。
1. 核心概念解析
创建 Three.js 3D 场景,必须包含三大核心组件,缺一不可:
组件 | 核心作用 | 通俗类比 |
Scene(场景) | 承载 3D 物体、灯光、相机的 "容器" | 电影的「拍摄场地」 |
Camera(相机) | 决定观察视角(从哪个角度看场景) | 电影的「摄像机」 |
Renderer(渲染器) | 将场景渲染到浏览器页面 | 电影的「放映机」 |
2. 完整代码实现(创建 ThreeDemo.vue 组件)
在src/components目录下新建ThreeDemo.vue文件,代码附带详细注释,便于理解每一步作用:
<template><!-- 3D场景容器:必须设置宽高,否则渲染器无法正常显示 --><div class="three-container" ref="container"></div>
</template><script setup>
import { ref, onMounted, onUnmounted } from 'vue'
// 导入Three.js核心模块(* as THREE表示导入所有核心功能)
import * as THREE from 'three'// 声明核心变量:容器引用、场景/相机/渲染器实例、立方体对象、动画循环ID
const container = ref(null)
let scene, camera, renderer, cube, animationId// 初始化3D场景的核心函数
const initScene = () => {// --------------------------// 步骤1:创建场景(Scene)// --------------------------scene = new THREE.Scene()// 可选:设置场景背景色(默认黑色,这里设为浅灰色提升视觉体验)scene.background = new THREE.Color(0xf0f0f0)// --------------------------// 步骤2:创建透视相机(模拟人眼视角)// 参数说明:fov(视角角度)、aspect(宽高比)、near(近裁剪面)、far(远裁剪面)// --------------------------const containerWidth = container.value.clientWidthconst containerHeight = container.value.clientHeightcamera = new THREE.PerspectiveCamera(75, containerWidth / containerHeight, 0.1, 1000)// 调整相机位置:默认在(0,0,0),与立方体重叠会看不到,故沿z轴后移5个单位camera.position.z = 5// --------------------------// 步骤3:创建WebGL渲染器// --------------------------renderer = new THREE.WebGLRenderer({ antialias: true }) // antialias开启抗锯齿,画面更平滑renderer.setSize(containerWidth, containerHeight) // 渲染尺寸与容器一致// 将渲染器生成的canvas元素,添加到页面容器中container.value.appendChild(renderer.domElement)// --------------------------// 步骤4:创建3D物体(立方体+彩色材质)// --------------------------// 4.1 几何体:BoxGeometry(宽, 高, 深),定义立方体的形状const cubeGeometry = new THREE.BoxGeometry(2, 2, 2)// 4.2 材质:MeshNormalMaterial自带彩色效果,无需额外灯光const cubeMaterial = new THREE.MeshNormalMaterial({wireframe: false, // 是否显示线框(true时仅显示轮廓,适合调试)transparent: false, // 是否透明opacity: 1 // 透明度(0-1,仅transparent为true时生效)})// 4.3 网格对象:几何体+材质的组合,是Three.js中可渲染的3D物体cube = new THREE.Mesh(cubeGeometry, cubeMaterial)// 将立方体添加到场景中(未添加则不会被渲染)scene.add(cube)// --------------------------// 步骤5:添加动画循环(实现立方体旋转)// --------------------------const animate = () => {// 请求浏览器下一帧动画(浏览器优化机制,避免卡顿)animationId = requestAnimationFrame(animate)// 每帧更新立方体旋转角度(x轴、y轴各旋转0.01弧度)cube.rotation.x += 0.01cube.rotation.y += 0.01// 渲染场景:将"场景"通过"相机"投射到页面renderer.render(scene, camera)}// 启动动画循环animate()
}// 窗口大小自适应函数(避免窗口缩放后场景变形)
const handleWindowResize = () => {if (!camera || !renderer || !container.value) return// 更新容器宽高const newWidth = container.value.clientWidthconst newHeight = container.value.clientHeight// 更新相机宽高比,并重新计算投影矩阵(必须执行,否则视角会变形)camera.aspect = newWidth / newHeightcamera.updateProjectionMatrix()// 更新渲染器尺寸renderer.setSize(newWidth, newHeight)
}// Vue生命周期钩子:确保DOM加载完成后初始化场景
onMounted(() => {initScene()// 监听窗口缩放事件window.addEventListener('resize', handleWindowResize)
})// Vue生命周期钩子:组件销毁时清理资源(避免内存泄漏)
onUnmounted(() => {// 移除窗口缩放监听window.removeEventListener('resize', handleWindowResize)// 停止动画循环cancelAnimationFrame(animationId)// 销毁渲染器(释放GPU资源)renderer.dispose()
})
</script><style scoped>
/* 3D容器必须设置宽高,否则渲染器生成的canvas会是默认的100x100像素 */
.three-container {width: 100vw;height: 80vh;margin-top: 20px;
}
</style>
3. 在 App.vue 中引用组件
修改src/App.vue,引入并使用上述创建的ThreeDemo组件,实现页面入口:
<template><div id="app"><h1 style="text-align: center; margin-top: 20px;">Three.js 第一个3D场景</h1><!-- 引入3D场景组件 --><ThreeDemo /></div>
</template><script setup>
// 导入ThreeDemo组件
import ThreeDemo from './components/ThreeDemo.vue'
</script><style>
/* 全局样式重置:消除默认边距,避免页面布局偏移 */
body {margin: 0;padding: 0;
}
</style>
4. 运行效果预览
启动项目后访问http://localhost:5173,可看到以下效果:
- 浅灰色背景的 3D 场景
- 一个彩色立方体沿 x 轴、y 轴缓慢旋转
- 拖动窗口调整大小,场景会自动适配,无变形
四、新手必看:关键注意事项
- 容器宽高不可省略:渲染器依赖容器宽高确定 canvas 尺寸,未设置会导致场景显示异常(默认 100x100 像素)。
- 相机位置需合理调整:相机默认在 (0,0,0),若与物体重叠会 "看不到" 物体,需通过camera.position调整(如沿 z 轴后移)。
- 材质选择适配场景:
- MeshBasicMaterial:基础材质,不依赖灯光,适合调试。
- MeshNormalMaterial:彩色自发光材质,无需额外灯光,适合入门演示。
- 后续将讲解依赖灯光的材质(如MeshLambertMaterial)。
4.组件销毁需清理资源:必须停止动画循环(cancelAnimationFrame)并销毁渲染器(renderer.dispose),避免内存泄漏。
五、专栏预告
下一篇将深入 Three.js 的几何体与材质系统,内容包括:
- 常用几何体(平面、球体、圆柱体等)的创建与参数配置
- 材质分类与应用场景(基础材质、光照材质、纹理材质)
- 实战:创建带纹理贴图的 3D 球体场景
若操作中遇到问题,可在评论区留言,后续将优先针对高频问题补充说明!