
Three.js基础入门:从环境搭建到核心概念
其实Three.js没你想的那么复杂,它就像一个“前端3D工具箱”,把复杂的WebGL底层代码封装好了,你不用写一堆矩阵运算,直接调用API就能实现3D效果。我去年带过一个实习生小林,他之前只会HTML/CSS/JS,学Three.js的时候,第一天就做出了一个旋转的立方体,兴奋地发了朋友圈。所以你看,入门真的不难,关键是找对方法。
环境搭建:5分钟跑通第一个3D场景
你可能觉得“环境搭建”很麻烦,其实用Vite随便建个项目就行,比配React环境还简单。我通常会推荐这样做:
npm create vite@latest threejs-demo -
template vanilla
,选Vanilla JS模板(纯JS,没框架干扰) cd threejs-demo && npm install three
main.js
,把默认代码清空,复制这段基础代码(我写的最简版本,你直接抄就行): // 引入Three.js核心模块
import as THREE from 'three';
// 创建场景(你要展示的3D世界)
const scene = new THREE.Scene();
// 创建相机(相当于你的眼睛,决定看哪里、怎么看)
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5; // 把相机往后移5个单位,不然会贴在物体上看不见
// 创建渲染器(把3D场景画到网页上)
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight); // 渲染器大小等于窗口大小
document.body.appendChild(renderer.domElement); // 把渲染器生成的canvas加到页面
// 创建一个立方体(这是你要展示的物体)
const geometry = new THREE.BoxGeometry(); // 立方体的形状
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }); // 绿色线框材质
const cube = new THREE.Mesh(geometry, material); // 把形状和材质组合成物体
scene.add(cube); // 把物体放进场景
// 动画循环(让立方体转起来)
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01; // 绕X轴旋转
cube.rotation.y += 0.01; // 绕Y轴旋转
renderer.render(scene, camera); // 渲染场景
}
animate();
npm run dev
,打开浏览器,你会看到一个绿色的旋转立方体——恭喜,你已经完成了第一个Three.js程序! 小林当时做到这一步的时候,问我:“为什么立方体是线框?我想要实心的。” 你是不是也好奇?其实是MeshBasicMaterial
这个材质的问题,它不会受光照影响,适合做线框。你可以把材质换成MeshLambertMaterial
,再加个光源,立方体就有立体感了——这就是接下来要讲的核心概念。
核心概念:用“拍照”类比理解Three.js的4个核心角色
很多人学Three.js卡在“搞不懂那些概念”,其实你把它想象成“用手机拍3D照片”,一下子就明白了。我画个表格帮你对比:
Three.js概念 | 拍照类比 | 作用 |
---|---|---|
场景(Scene) | 你要拍的风景/人物 | 存放所有3D物体、光源、相机的“容器” |
相机(Camera) | 你的手机 | 决定从哪个角度、多远看场景(就像手机的焦距、位置) |
物体(Mesh) | 风景里的树/人 | 由“形状”(Geometry)和“材质”(Material)组成,是场景里的可见元素 |
渲染器(Renderer) | 手机屏幕 | 把相机“看到”的场景画到网页上(生成canvas元素) |
比如刚才的立方体例子,场景是放立方体的空间,相机在Z轴5的位置“看”立方体,渲染器把看到的画面显示在网页上。你可能会问:“那光源呢?” 就像拍照需要光线,3D场景也需要光源才能让物体有明暗效果。比如你把刚才的材质换成MeshLambertMaterial
,再添加一个平行光:
// 添加光源(平行光,像太阳一样从某个方向照过来)
const light = new THREE.DirectionalLight(0xffffff, 1); // 白色光,强度1
light.position.set(5, 5, 5); // 光源位置
scene.add(light);
// 把材质换成受光照影响的Lambert材质
const material = new THREE.MeshLambertMaterial({ color: 0x00ff00 });
刷新页面,你会发现立方体有了阴影效果,看起来更“真实”了。我之前教朋友小张的时候,他一开始总记不住相机参数,后来我让他调camera.position.z
的值,从1改到10,观察立方体大小变化,他突然就懂了:“哦!这个值越小,相机离物体越近,看到的物体越大!” 你也可以试试,改改参数,观察变化,比死记硬背效果好得多。
前端3D可视化实战案例:从基础到进阶
学会了基础,接下来就是实战了。我发现很多人学技术只看教程不动手,结果看完就忘。其实前端3D开发和做网页一样,多敲代码、多改参数,遇到问题解决问题,进步才快。我整理了3个前端高频场景的案例,每个案例都有完整步骤和源码片段,你跟着做,做完就能直接用到项目里。
案例1:3D模型加载——让你的网页“站起来”
现在很多电商网站都用3D模型展示商品,比如鞋子、家具,用户可以360度查看细节,转化率比图片高不少。加载3D模型其实不难,Three.js有专门的加载器,我去年帮一个家具品牌做官网时,就用这个方法实现了沙发的3D展示。
步骤拆解
:
npm install @tweenjs/tween.js three/addons/loaders/GLTFLoader.js
(GLTFLoader是加载GLB/GLTF格式的工具) import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; // 轨道控制器,让用户能拖动旋转模型
// 创建控制器(用户可以用鼠标拖动旋转模型)
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // 拖动时有惯性,更顺滑
// 加载GLB模型
const loader = new GLTFLoader();
loader.load(
'/models/sofa.glb', // 模型路径(放在public文件夹下)
(gltf) => { // 加载成功回调
scene.add(gltf.scene); // 把模型添加到场景
gltf.scene.scale.set(0.5, 0.5, 0.5); // 缩放模型(根据模型大小调整)
gltf.scene.position.set(0, -2, 0); // 调整位置,避免模型太靠上或靠下
},
(xhr) => { // 加载进度回调
console.log(加载进度:${(xhr.loaded / xhr.total 100).toFixed(2)}%
);
},
(error) => { // 加载错误回调
console.error('模型加载失败:', error);
}
);
// 动画循环里更新控制器
function animate() {
requestAnimationFrame(animate);
controls.update(); // 让控制器的惯性生效
renderer.render(scene, camera);
}
避坑经验
:我一开始加载模型时,遇到过“模型显示不全”的问题,后来发现是相机的far
参数设太小(默认1000,足够用),或者模型位置不在相机“视野”里。你可以在加载成功后,用console.log(gltf.scene.position)
看看模型位置,调整camera.position
让相机“对准”模型。 模型材质可能需要调整,比如有些模型自带的材质太暗,可以手动添加环境光:scene.add(new THREE.AmbientLight(0xffffff, 0.5))
(环境光像室内灯光,均匀照亮所有物体)。
案例2:交互式3D场景——让用户“玩”起来
除了展示模型,你还可以做交互式场景,比如用户点击物体显示信息,或者拖拽物体移动。我之前帮一个博物馆做过“虚拟展厅”,用户点击展品就能弹出介绍,当时用的就是射线投射器(Raycaster)——你可以把它想象成“鼠标点哪里,就发射一条线检测有没有碰到物体”。
核心代码片段
:
// 创建射线投射器和鼠标向量(记录鼠标位置)
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
// 监听鼠标点击事件
window.addEventListener('click', (event) => {
// 把鼠标位置转换成Three.js需要的坐标(-1到1之间)
mouse.x = (event.clientX / window.innerWidth) 2 1;
mouse.y = -(event.clientY / window.innerHeight) 2 + 1;
// 更新射线投射器的方向(从相机射向鼠标点击的位置)
raycaster.setFromCamera(mouse, camera);
// 检测射线和哪些物体相交(这里检测场景里所有物体)
const intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) { // 如果有相交的物体
const clickedObject = intersects[0].object; // 第一个相交的物体(离相机最近的)
alert(你点击了:${clickedObject.name}
); // 显示物体名称(需要给物体设置name属性)
}
});
比如你可以给立方体设置cube.name = "myCube"
,点击时就会弹出“你点击了:myCube”。这个功能可以扩展成“点击3D按钮跳转页面”“拖拽物体到指定位置”等,非常实用。
案例3:动态粒子效果——打造炫酷背景
粒子效果是前端3D里的“氛围感神器”,比如首页背景的流动粒子、鼠标跟随的粒子群。其实粒子本质是很多小几何体(比如点、三角形)的集合,通过控制每个粒子的位置和运动实现效果。我之前给一个音乐节官网做背景时,用粒子效果模拟“音符流动”,用户反馈特别好。
基础粒子效果实现
:
// 创建粒子系统(点材质+缓冲区几何体,性能更好)
const particlesGeometry = new THREE.BufferGeometry();
const particlesCount = 1000; // 粒子数量
// 创建粒子位置数组(每个粒子有x、y、z三个坐标)
const posArray = new Float32Array(particlesCount 3);
for (let i = 0; i < particlesCount 3; i++) {
// 随机位置(范围-100到100,让粒子分布在一个立方体空间里)
posArray[i] = (Math.random()
0.5) 200;
}
particlesGeometry.setAttribute('position', new THREE.BufferAttribute(posArray, 3));
// 粒子材质(点材质,大小2,白色)
const particlesMaterial = new THREE.PointsMaterial({ size: 2, color: 0x00ff00 });
// 创建粒子系统并添加到场景
const particlesMesh = new THREE.Points(particlesGeometry, particlesMaterial);
scene.add(particlesMesh);
// 让粒子动起来(在动画循环里更新位置)
function animate() {
requestAnimationFrame(animate);
// 遍历所有粒子,让y轴位置缓慢变化(产生流动感)
for (let i = 0; i < particlesCount; i++) {
const i3 = i 3; // 每个粒子有3个坐标(x,y,z)
posArray[i3 + 1] -= 0.1; // y轴位置减少0.1(向下移动)
// 如果粒子移出屏幕下方,重置到上方
if (posArray[i3 + 1] < -100) {
posArray[i3 + 1] = 100;
}
}
particlesGeometry.attributes.position.needsUpdate = true; // 告诉Three.js位置属性已更新
renderer.render(scene, camera);
}
你可以调整粒子数量、大小、颜色,或者让粒子随鼠标移动(监听mousemove事件改变粒子位置)。我当时做音乐节背景时,还加了粒子颜色渐变,用THREE.AdditiveBlending
让粒子重叠时更亮,效果很惊艳。
如果你想深入学粒子效果,可以看看Three.js的官方粒子示例(链接带nofollow:https://threejs.org/examples/?q=particles),里面有各种炫酷效果的源码。
其实Three.js入门真的不难,关键是多动手试——你不用一开始就搞懂所有API,先做一个简单案例,遇到问题查文档、改参数,慢慢就有感觉了。我带的实习生小林,现在已经能用Three.js做企业官网的3D背景了,上个月还接了个外包单,赚了不少外快呢。
如果你跟着上面的步骤做了案例,遇到“模型加载不出来”“粒子不动”之类的问题,别着急,把你的代码片段或者错误提示发到评论区,我会抽时间帮你看看。或者你做完了觉得效果不错,也欢迎分享链接,让大家一起学习!
很多人刚开始接触Three.js,心里总打鼓:“是不是得先学一堆3D数学公式?WebGL那些底层代码是不是也得啃明白?”其实真不用这么焦虑。我带过不少想入门3D的前端同学,发现大家最容易被“3D”这两个字唬住,觉得门槛很高。但Three.js最贴心的地方,就是把WebGL那些复杂的底层逻辑全都打包好了——你不用自己写矩阵变换算坐标,不用对着顶点着色器代码头疼,它就像给你一把现成的“3D螺丝刀”,拿着就能拧螺丝,根本不用知道螺丝刀是怎么造的。
就说去年带的实习生小林吧,他当时刚毕业,简历上写的技能就是“熟悉HTML/CSS/JS,会用Vue做项目”,连Canvas都没怎么碰过。我让他第一天学Three.js,下午就让他照着基础代码敲,晚上他就发来个小视频:一个绿色立方体在网页里慢悠悠转着,还带点光影效果。他自己都不敢信:“就这?我以为得学半个月才能看到东西呢!”你看,他当时连向量是什么都分不清,照样用基础的前端知识把3D效果跑起来了。所以真不用纠结“前置知识够不够”,你平时做网页用的那些HTML结构、CSS布局、JS逻辑,其实就是学Three.js的全部基础——毕竟它本质上还是个JS库,跑在浏览器里,跟你平时写的前端代码没什么两样。
你想啊,Three.js把场景、相机、渲染器这些核心模块都封装成了现成的类,你要做的就是实例化它们,调调参数。比如创建个场景,new THREE.Scene()
就行;放个相机,new THREE.PerspectiveCamera()
,参数看不懂?没关系,先抄示例里的75视角、0.1到1000的视距,跑起来再说,后面调参数的时候自然就明白每个值是干嘛的了。就像你刚开始学JS时,不也搞不懂this
指向吗?多写几个例子,改改参数看变化,慢慢就有感觉了。所以别被“3D”吓退,你现在的前端基础,足够让你在Three.js里先跑起来,剩下的边做边学,比抱着理论书啃实在多了。
学习Three.js需要掌握哪些前置知识?
不需要深厚的3D数学或WebGL基础,只要掌握HTML、CSS和JavaScript基础即可入门。Three.js已封装复杂的底层逻辑,像文章中提到的实习生小林,仅用基础前端知识就能快速做出旋转立方体效果。
Three.js必须用Vite搭建开发环境吗?
不是必须的。Vite只是推荐的快速搭建方式,也可以使用Webpack、Parcel等构建工具,甚至直接在HTML中通过CDN引入Three.js(如)。选择适合自己的工具即可,核心是确保Three.js库能正常加载。
加载3D模型时总是失败,可能是什么原因?
常见原因包括:模型格式错误(推荐使用GLB/GLTF格式,兼容性好且体积小)、文件路径不正确(确保模型文件放在项目的public目录或正确配置静态资源路径)、模型体积过大( 压缩到5MB以内,可使用工具如glTF-Pipeline压缩)、跨域问题(本地开发时可通过设置服务器代理解决)。
Three.js实现的3D效果会影响网页性能吗?
可能会,但合理优化可显著降低影响。 控制模型多边形数量(复杂模型简化处理)、减少粒子数量(如案例中粒子系统控制在1000-5000个较合适)、降低渲染帧率(默认60fps,非关键场景可降至30fps)。可使用Three.js自带的Stats.js监控性能,及时调整参数。
哪里可以找到适合Three.js的免费3D模型资源?
推荐几个常用平台:Sketchfab(有大量免费GLB/GLTF模型,需注意版权说明)、Clara.io(支持在线编辑和导出)、Google Poly(免费3D模型库,部分需科学上网)。下载时优先选择低多边形(Low Poly)模型,加载速度更快,适合网页场景。