3D 汽车颜色定制:Vue.js + Three.js 实战指南如何在Vue中使用Three.js?本文将带友友们在vu
前言
Three.js 是一个流行的 JavaScript 库,简化了 WebGL 的使用,让我们能够在浏览器中轻松创建和展示 3D 图形。今天带友友们在Vue中使用Three.js,初步构建出功能丰富且易于维护的 3D Web 应用程序。本文将通过一个具体的例子——一个允许用户选择汽车颜色的交互式 3D, 展示如何在vue项目中使用 Three.js ,先上效果:
一:思路概述
我们的目标是创建一个基于 Web 的 3D 汽车展示应用,用户可以在这个应用中自由地改变汽车的颜色。为了实现这一目标,我们需要解决以下几个问题:
- 设置 Three.js 环境:初始化 Three.js 的基本组件,如场景、相机、渲染器等。
- 加载 3D 模型:使用 Three.js 的加载器加载外部 3D 模型。
- 交互设计:允许用户通过 UI 控制 3D 模型的状态。
- 性能优化:确保应用程序在不同的设备上都能流畅运行。
二:初始化 Three.js
2.1:场景和相机
首先,我们需要创建一个 Three.js 场景,并初始化一个透视相机。透视相机模仿人眼的视角,使远处的物体看起来更小。
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(3, 2, 2); // 设置相机位置
- 这里的
40
表示视场角,决定了相机视角的宽度; window.innerWidth / window.innerHeight
计算屏幕的宽高比,确保场景正确填充窗口;0.1
和1000
分别是近裁剪平面和远裁剪平面的距离,超出这两个距离的对象不会被渲染。
2.2:渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth * 0.9, window.innerHeight * 0.8);
document.body.appendChild(renderer.domElement);
WebGLRenderer
是 Three.js 中用于渲染场景的主要类。这里设置了抗锯齿 (antialias
) 和透明度支持 (alpha
)。setSize
方法用于设置渲染器输出的尺寸,使其适应屏幕大小。- 最后把渲染器的 DOM 元素添加到页面中。
2.3:动画循环
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
requestAnimationFrame
方法用于请求浏览器在下一次重绘之前调用指定的回调函数。这样可以保证渲染器在每一帧都更新场景,从而创建出动画效果。
三:加载 3D 模型
3.1:导入模型
为了能够看到一辆真实的汽车,我们需要加载一个 3D 模型。在这个例子中使用的是 .glb
格式的模型,它是一种包含了模型的所有信息(几何体、纹理等)的紧凑格式。
const loader = new THREE.GLTFLoader();
const dracoLoader = new THREE.DRACOLoader();
dracoLoader.setDecoderPath("../../public/roadSter/draco/gltf/");
loader.setDRACOLoader(dracoLoader);
GLTFLoader
负责加载.glb
文件DRACOLoader
则用于解压模型数据中的 Draco 压缩格式。Draco 是一种高效的 3D 压缩算法,能够显著减小 3D 模型的文件大小。
3.2:模型处理
加载模型后,我们还需要对模型进行一些处理,例如更改材质或调整位置等。
loader.load("../../public/roadSter/model/bmw01.glb", gltf => {
const bmw = gltf.scene;
bmw.traverse(child => {
if (child.isMesh) {
// 根据模型的命名规则来更改材质
switch (child.name) {
case "轮毂":
child.material = wheelsMaterial;
break;
case "车身":
child.material = bodyMaterial;
break;
// 更多的材质更改...
}
}
});
scene.add(bmw);
});
这里遍历了模型的每个网格对象,并根据它们的名字来更改相应的材质。traverse
方法用于遍历场景图中的所有对象,这对于查找特定类型的节点非常有用。
四:交互设计
为了能与 3D 模型互动,需要添加一些 UI 控件,这里以添加一个颜色选择器为例,让用户可以选择不同的汽车颜色。
const selectColor = color => {
bodyMaterial.color.set(color);
// 更改其他相关材质的颜色...
};
let colors = [
{
name: "冷光银",
color: "#424449"
},
// 更多颜色选项...
];
定义了一个 selectColor
函数,当用户选择一个颜色时,这个函数会被调用来更新汽车的材质颜色。同时,我们创建了一个 colors
数组来存储可用的颜色选项。
在 Vue.js 的模板中,使用 v-for
指令来动态生成颜色选择按钮。
<div class="select">
<div
class="select-item"
v-for="(item, index) in colors"
:key="index"
@click="selectColor(item.color)"
>
<div class="select-item-color" :style="{ background: item.color }"></div>
<div class="select-item-name">{{ item.name }}</div>
</div>
</div>
这里使用了 Vue.js 的数据绑定特性,当用户点击某个颜色时,对应的事件处理器将会被触发,进而改变汽车的颜色。
五:性能优化
开发过程中,性能问题也是十分主要的一个点,特别是在我们的这个3D交换渲染过程,这里采用缓存来减少加载时间,或者使用适当的光照来提高渲染效率。
// 使用缓存来减少重复加载相同的模型
loader.loadModel = function(url) {
return new Promise((resolve, reject) => {
loader.load(url, resolve, undefined, reject);
});
};
// 缓存模型加载
const loadModel = async url => {
return await loader.loadModel(url);
};
此外,还可以考虑使用 THREE.LoadingManager
来管理加载过程,或者使用 THREE.LoadingScreen
来显示加载进度条。这些可以提高前端用户的体验,特别是在网络条件不佳的情况下。
六:结构与组件化
将 3D 汽车展示的部分封装成一个独立的 Vue 组件,这样可以方便地复用代码并保持代码的清晰性。
- 利用 Vue.js 的组合 API 来组织代码
onMounted
钩子确保在组件挂载后执行 Three.js 的初始化代码
// 3DCar.vue
<template>
<div ref="canvasContainer" />
</template>
<script>
import { onMounted, ref } from 'vue';
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
export default {
setup() {
const canvasContainer = ref(null);
onMounted(() => {
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(3, 2, 2);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth * 0.9, window.innerHeight * 0.8);
canvasContainer.value.appendChild(renderer.domElement);
const loader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('../../public/roadSter/draco/gltf/');
loader.setDRACOLoader(dracoLoader);
loader.load('../../public/roadSter/model/bmw01.glb', gltf => {
const bmw = gltf.scene;
scene.add(bmw);
});
const animate = () => {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
animate();
});
return {
canvasContainer,
};
},
};
</script>
转载自:https://juejin.cn/post/7402926506543054859