Vue.js - 集成Three.js构建三维可视化场景教程1(安装配置、基本用法)
作者:hangge | 2026-05-14 08:40
Three.js 是一个强大的 WebGL 库,能够帮助开发者轻松创建和展示 3D 图形。结合 Vue.js 的响应式特性,我们可以构建出既美观又交互性强的 3D Web 应用。本文将演示如何在 Vue.js 项目中集成 Three.js,并创建一个简单的 3D 场景。
(2)运行效果如下,立法体会在场景中自动不断地自转。
一、安装配置与基本用法
1,安装配置
进入 Vue 项目目录,执行如下命令安装 Three.js 核心库。
npm install three
2,基本用法
(1)下面是一个简单的使用样例,里面包含场景、相机、渲染器、光源、一个旋转立方体、OrbitControls、窗口缩放响应、资源销毁与动画循环管理等内容。
<template>
<!--
Three.js 渲染容器
WebGLRenderer 创建的 canvas 会被手动 append 到这个 div 中
Vue 本身不直接管理 canvas
-->
<div class="three-container" ref="container"></div>
</template>
<script>
// 引入 Three.js 核心库
import * as THREE from 'three'
// 引入轨道控制器(鼠标旋转 / 缩放 / 平移视角)
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
export default {
name: 'ThreeScene',
// ====== 组件参数 ======
props: {
// 是否启用 OrbitControls
enableControls: {
type: Boolean,
default: true
},
// 指定渲染宽度(可选)
// 如果不传,则自动使用容器尺寸
width: {
type: Number,
default: null
},
// 指定渲染高度(可选)
height: {
type: Number,
default: null
}
},
// ====== Three.js 核心对象(不做响应式) ======
data() {
return {
scene: null, // Three.js 场景
camera: null, // 相机
renderer: null, // WebGL 渲染器
controls: null, // 轨道控制器
rafId: null, // requestAnimationFrame 返回的 ID,用于停止动画
cube: null // 示例立方体 Mesh
}
},
// ====== 组件挂载完成 ======
mounted() {
// 确保 DOM 已经渲染完成
this.$nextTick(() => {
this.initThree() // 初始化 Three.js 场景
this.start() // 启动渲染循环
// 监听窗口尺寸变化(用于自适应)
window.addEventListener('resize', this.onWindowResize, { passive: true })
// 监听页面可见性(切换标签页时暂停渲染)
document.addEventListener('visibilitychange', this.onVisibilityChange)
})
},
// ====== 组件销毁前 ======
beforeDestroy() {
// 停止动画循环
this.stop()
// 移除全局事件监听
window.removeEventListener('resize', this.onWindowResize)
document.removeEventListener('visibilitychange', this.onVisibilityChange)
// 释放 Three.js 所有资源(非常关键)
this.disposeThree()
},
methods: {
/**
* 初始化 Three.js 场景
* 包含:场景、相机、渲染器、光源、物体、控制器
*/
initThree() {
const container = this.$refs.container
// ===== 1) 创建场景 =====
this.scene = new THREE.Scene()
// 设置背景色(深色背景更适合 3D)
this.scene.background = new THREE.Color(0x111111)
// ===== 2) 创建相机 =====
// 透视相机(人眼视角)
const aspect = this.getWidth() / this.getHeight()
this.camera = new THREE.PerspectiveCamera(
60, // 视野角度(FOV)
aspect, // 宽高比
0.1, // 近裁剪面
1000 // 远裁剪面
)
// 设置相机位置
this.camera.position.set(0, 5, 5)
// ===== 3) 创建渲染器 =====
this.renderer = new THREE.WebGLRenderer({
antialias: true // 抗锯齿
})
// 设置像素比,防止高分屏过度消耗性能
this.renderer.setPixelRatio(
Math.min(window.devicePixelRatio || 1, 2)
)
// 设置渲染尺寸
this.renderer.setSize(this.getWidth(), this.getHeight())
// 使用 sRGB 颜色空间,避免颜色偏暗
this.renderer.outputEncoding = THREE.sRGBEncoding
// 开启阴影支持
this.renderer.shadowMap.enabled = true
// 将 canvas 插入到容器中
container.appendChild(this.renderer.domElement)
// ===== 4) 光源系统 =====
this.scene.add(new THREE.AmbientLight(0xffffff, 1.0)) // 环境光(核心)
const dir = new THREE.DirectionalLight(0xffffff, 2.0) // 主光
dir.position.set(10, 20, 10)
dir.castShadow = true
dir.shadow.mapSize.width = 1024
dir.shadow.mapSize.height = 1024
this.scene.add(dir)
const back = new THREE.DirectionalLight(0xffffff, 0.5) // 侧逆光(提轮廓)
back.position.set(-10, 10, -10)
this.scene.add(back)
// ===== 5) 地面(接收阴影) =====
const groundGeo = new THREE.PlaneGeometry(5, 5)
const groundMat = new THREE.MeshStandardMaterial({
color: 0xcccccc
})
const ground = new THREE.Mesh(groundGeo, groundMat)
// 平面默认是竖直的,需要旋转为水平
ground.rotation.x = -Math.PI / 2
ground.position.y = -1.0
ground.receiveShadow = true
this.scene.add(ground)
// ===== 6) 示例物体(立方体) =====
const geo = new THREE.BoxGeometry(1, 1, 1)
const mat = new THREE.MeshStandardMaterial({
color: 0x2194ce
})
this.cube = new THREE.Mesh(geo, mat)
this.cube.castShadow = true
this.scene.add(this.cube)
// ===== 7) 轨道控制器 =====
if (this.enableControls) {
this.controls = new OrbitControls(
this.camera,
this.renderer.domElement
)
// 开启阻尼(惯性效果)
this.controls.enableDamping = true
}
},
/**
* 渲染循环(每一帧都会执行)
*/
animate() {
// 示例动画:立方体自转
if (this.cube) {
this.cube.rotation.x += 0.01
this.cube.rotation.y += 0.015
}
// 更新控制器(阻尼效果必须)
if (this.controls) this.controls.update()
// 执行渲染
this.renderer.render(this.scene, this.camera)
// 请求下一帧
this.rafId = requestAnimationFrame(this.animate)
},
/**
* 启动动画循环
*/
start() {
if (!this.rafId) {
this.rafId = requestAnimationFrame(this.animate)
}
},
/**
* 停止动画循环
*/
stop() {
if (this.rafId) {
cancelAnimationFrame(this.rafId)
this.rafId = null
}
},
/**
* 获取渲染宽度
* 优先使用 props,其次使用容器尺寸
*/
getWidth() {
if (this.width) return this.width
return this.$refs.container.clientWidth || window.innerWidth
},
/**
* 获取渲染高度
*/
getHeight() {
if (this.height) return this.height
return this.$refs.container.clientHeight || window.innerHeight
},
/**
* 窗口尺寸变化处理
* 必须同时更新相机和渲染器
*/
onWindowResize() {
if (!this.camera || !this.renderer) return
const w = this.getWidth()
const h = this.getHeight()
this.camera.aspect = w / h
this.camera.updateProjectionMatrix()
this.renderer.setSize(w, h)
this.renderer.setPixelRatio(
Math.min(window.devicePixelRatio || 1, 2)
)
},
/**
* 页面可见性变化处理
* 切换标签页时暂停渲染,节省 CPU / GPU
*/
onVisibilityChange() {
if (document.hidden) {
this.stop()
} else {
this.start()
}
},
/**
* 彻底释放 Three.js 资源(防止内存 / 显存泄露)
*/
disposeThree() {
// 停止动画
this.stop()
// 释放控制器
if (this.controls) {
this.controls.dispose()
this.controls = null
}
// 遍历场景,释放 Mesh 资源
if (this.scene) {
this.scene.traverse(obj => {
if (!obj.isMesh) return
// 几何体
if (obj.geometry) {
obj.geometry.dispose()
}
// 材质(可能是数组)
if (obj.material) {
const mat = obj.material
if (Array.isArray(mat)) {
mat.forEach(m => {
if (m.map) m.map.dispose()
m.dispose()
})
} else {
if (mat.map) mat.map.dispose()
mat.dispose()
}
}
})
}
// 释放渲染器(非常关键)
if (this.renderer) {
this.renderer.forceContextLoss()
this.renderer.domElement && this.renderer.domElement.remove()
this.renderer = null
}
// 清空引用
this.scene = null
this.camera = null
this.cube = null
}
}
}
</script>
<style scoped>
/* Three.js 容器样式 */
.three-container {
width: 100%;
height: 100%;
min-height: 360px;
position: relative;
overflow: hidden;
}
</style>
(2)运行效果如下,立法体会在场景中自动不断地自转。

(3)同时由于样例中启用了 OrbitControls,我们可以通过鼠标对三维场景进行直观交互(整体交互体验接近专业三维软件(如 CAD、建模工具)的操作方式):
- 按住鼠标左键拖动可围绕目标物体旋转视角,从不同角度观察模型。
- 滚动鼠标滚轮可实现视图的放大与缩小,即相机向目标点靠近或远离。
- 按住鼠标右键拖动可平移视角,使整个场景在屏幕中上下左右移动。
- 同时开启了阻尼效果,松开鼠标后相机会产生轻微的惯性滑动并逐渐停止。

全部评论(0)