Vue3+Three.js实现

一、环境准备

1. 初始化 Vue3 项目

首先确保你已安装 Node.js(建议 v16+),然后通过 Vite 快速创建 Vue3 项目:

# 创建 Vue3 项目(选 Vue + TypeScript 或 JavaScript 均可)
npm create vite@latest vue3-three-demo -- --template vue
cd vue3-three-demo
npm install

2. 安装核心依赖

需要安装 Three.js 核心库和轨道控制器(Three.js 128+ 版本后控制器单独拆分):

# 安装 Three.js 核心 + 轨道控制器npm install three @tweenjs/tween.js
  • three:Three.js 核心库(3D 渲染核心)
  • @tweenjs/tween.js:可选,用于实现 3D 物体动画(本文会用到)

二、核心概念速览(必懂)

在写代码前,先记住 Three.js 3 个核心要素(缺少一个都无法渲染):

  • 场景(Scene):3D 世界的容器,所有 3D 物体、光照都要放在场景中
  • 相机(Camera):相当于人的眼睛,决定从哪个角度观察场景
  • 渲染器(Renderer):将场景和相机的内容渲染到页面 DOM 元素上
其他常用概念:
  • 几何体(Geometry):3D 物体的形状(如立方体、球体)
  • 材质(Material):3D 物体的外观(颜色、纹理、光泽)
  • 网格(Mesh):几何体 + 材质的组合(真正可渲染的 3D 物体)
  • 光照(Light):让 3D 物体有明暗层次(否则是纯色块)
  • 控制器(OrbitControls):让用户可以拖拽、缩放、旋转场景

三、编写第一个 3D 项目

创建 src/components/ThreeDemo.vue 组件,复制以下代码(含详细注释):

<template>
    <!-- 3D 渲染容器 -->
    <div class="three-container" ref="containerRef"></div>
</template>
<script setup lang="ts">
    import { ref, onMounted, onUnmounted } from 'vue';
    import * as THREE from 'three';
    import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
    import { Tween } from '@tweenjs/tween.js';
    // 核心对象引用
    const containerRef = ref<HTMLDivElement | null>(null);
    let scene: THREE.Scene, camera: THREE.PerspectiveCamera, renderer: THREE.WebGLRenderer;
    let cube: THREE.Mesh, controls: OrbitControls;
    // 初始化 3D 场景
    const initThree = () => {
    if (!containerRef.value) return;
    // 1. 创建场景(容器)
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xf0f0f0); // 场景背景色(浅灰)
    // 2. 创建相机(透视相机:模拟人眼视角)
    // 参数:视野角度(75°)、宽高比、近裁切面、远裁切面
    camera = new THREE.PerspectiveCamera(
    75,
    containerRef.value.clientWidth / containerRef.value.clientHeight,
    0.1,
    1000
    );
    // 设置相机位置(x, y, z),默认在原点(0,0,0),需要拉开距离才能看到物体
    camera.position.z = 5;
    // 3. 创建渲染器(WebGL 渲染器,高性能)
    renderer = new THREE.WebGLRenderer({ antialias: true }); // 开启抗锯齿
    renderer.setSize(containerRef.value.clientWidth, containerRef.value.clientHeight); // 设置渲染尺寸
    containerRef.value.appendChild(renderer.domElement); // 将渲染画布添加到 DOM
    // 4. 创建 3D 物体(立方体)
    // 几何体:BoxGeometry(宽, 高, 深)
    const geometry = new THREE.BoxGeometry(2, 2, 2);
    // 材质:MeshLambertMaterial(漫反射材质,需要光照才能显示)
    const material = new THREE.MeshLambertMaterial({
    color: 0x42b983, // 基础颜色(绿色)
    wireframe: false, // 是否显示线框(true 可看到几何体结构)
});
    // 网格:几何体 + 材质
    cube = new THREE.Mesh(geometry, material);
    scene.add(cube); // 将立方体添加到场景
    // 5. 添加光照(否则物体是纯色块,无明暗)
    // 环境光:均匀照亮所有物体,无阴影
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
    scene.add(ambientLight);
    // 方向光:模拟太阳光,有阴影效果
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
    directionalLight.position.set(5, 5, 5); // 光源位置
    scene.add(directionalLight);
    // 6. 添加轨道控制器(允许用户交互:拖拽旋转、滚轮缩放)
    controls = new OrbitControls(camera, renderer.domElement);
    controls.enableDamping = true; // 开启阻尼效果(旋转更顺滑)
    controls.dampingFactor = 0.05;
    // 7. 添加动画(立方体自动旋转)
    const animateCube = () => {
    // 使用 Tween 实现平滑旋转动画
    new Tween(cube.rotation)
    .to({ x: cube.rotation.x + Math.PI * 2, y: cube.rotation.y + Math.PI * 2 }, 5000)
    .repeat(Infinity) // 无限循环
    .start();
};
    animateCube();
    // 8. 监听窗口大小变化(自适应调整)
    window.addEventListener('resize', onWindowResize);
};
    // 窗口大小自适应
    const onWindowResize = () => {
    if (!containerRef.value) return;
    // 更新相机宽高比
    camera.aspect = containerRef.value.clientWidth / containerRef.value.clientHeight;
    camera.updateProjectionMatrix(); // 更新相机投影矩阵
    // 更新渲染器尺寸
    renderer.setSize(containerRef.value.clientWidth, containerRef.value.clientHeight);
};
    // 渲染循环(Three.js 核心,持续渲染场景)
    const renderLoop = () => {
    requestAnimationFrame(renderLoop); // 浏览器帧循环
    controls.update(); // 阻尼效果需要持续更新控制器
    Tween.update(); // 更新动画
    renderer.render(scene, camera); // 渲染场景和相机
};
    // 生命周期钩子:组件挂载时初始化
    onMounted(() => {
    initThree();
    renderLoop();
});
    // 生命周期钩子:组件卸载时清理(防止内存泄漏)
    onUnmounted(() => {
    window.removeEventListener('resize', onWindowResize);
    controls.dispose(); // 销毁控制器
    renderer.dispose(); // 销毁渲染器
    // 清理场景中所有物体
    scene.traverse((obj) => {
    if (obj instanceof THREE.Mesh) {
    obj.geometry.dispose();
    (obj.material as THREE.Material).dispose();
}
});
});
</script>
<style scoped>
    .three-container {
    width: 100vw;
    height: 80vh;
    margin: 20px 0;
}
</style>

2. 在主组件中使用

修改 src/App.vue,引入并使用 ThreeDemo 组件:

<template>
    <div id="app">
        <h1>Vue3 + Three.js 第一个 3D 项目</h1>
        <ThreeDemo />
    </div>
</template>
<script setup lang="ts">
    import ThreeDemo from './components/ThreeDemo.vue';
</script>
<style scoped>
    #app {
    text-align: center;
    padding: 2rem;
}
</style>

四、运行项目

npm run dev
打开浏览器访问 http://localhost:5173 即可

五、进阶优化:添加纹理贴图

如果想让立方体更真实,可以给它添加纹理贴图(替换之前的纯色材质)。

1. 准备纹理图片

在 public 文件夹下放入一张纹理图片(如 texture.jpg,可自行下载)。

2. 修改材质代码

将之前的 MeshLambertMaterial 替换为带纹理的材质:

// 加载纹理(Three.js 内置纹理加载器)
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('/texture.jpg'); // 图片路径(public 下直接写文件名)
// 纹理优化(可选)
texture.wrapS = THREE.RepeatWrapping; // 水平重复
texture.wrapT = THREE.RepeatWrapping; // 垂直重复
texture.repeat.set(2, 2); // 重复次数(x 轴 2 次,y 轴 2 次)
texture.anisotropy = renderer.capabilities.getMaxAnisotropy(); // 各向异性过滤(提升纹理清晰度)
// 带纹理的材质
const material = new THREE.MeshLambertMaterial({
    map: texture, // 纹理贴图
});
重新运行项目,你会看到立方体表面贴上了纹理图片,更具真实感。

六、关键 API 总结(快速查阅)

功能
核心 API
创建场景
new THREE.Scene()
透视相机
new THREE.PerspectiveCamera(fov, aspect, near, far)
WebGL 渲染器
new THREE.WebGLRenderer({ antialias: true })
立方体几何体
new THREE.BoxGeometry(width, height, depth)
漫反射材质
new THREE.MeshLambertMaterial({ color, map })
3D 物体(网格)
new THREE.Mesh(geometry, material)
环境光
new THREE.AmbientLight(color, intensity)
方向光
new THREE.DirectionalLight(color, intensity)
轨道控制器
new OrbitControls(camera, renderer.domElement)
纹理加载
new THREE.TextureLoader().load(url)
动画循环
requestAnimationFrame(renderLoop)
复杂几何体:尝试 SphereGeometry(球体)、CylinderGeometry(圆柱体)等;
高级材质:MeshPhongMaterial(高光材质)、MeshStandardMaterial(PBR 物理材质);
模型加载:使用 GLTFLoader 加载 3D 模型(.gltf/.glb 文件);
粒子系统:创建大量小物体实现粒子特效(如星空、烟雾);
后期处理:添加阴影、模糊、泛光等特效;
Vue 生态整合:结合 Pinia 管理 3D 状态,结合 Vue Router 实现多场景切换;

 

360全景图three.js与Photo-Sphere-Viewer-master 3D全景浏览开发

1.支持WebGL和canvas的浏览器 (IE10, IE11支持, 但在IE里移动图片时很卡, 不一定是全部人都有这情况)

2.Three.js (文件较大, 有官网demo, 可不下载, 下载photo-sphere-viewer.js时也有three.js)

1
下载地址:https://github.com/mrdoob/three.js

3.photo-sphere-viewer.js (这是基于Three.js开发的柱状全景图插件)

1
下载地址:https://github.com/JeremyHeleine/Photo-Sphere-Viewer

4.360度全景图,是左右能够完美拼接的

1
参考: 图片尺寸 3600 * 1800 (最佳尺寸) http://www.360pano.eu/show/?id=400

5.引入js

1526536201-5375-5-20171127170330815-27454492

6.js调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var div = document.getElementById('container');
var PSV = new PhotoSphereViewer({
    // Path to the panorama
    panorama: 'images/0398.jpg',
    // Container
    container: div,
    // Deactivate the animation
    time_anim: false,
    // Display the navigation bar
    navbar: true,      
    // Resize the panorama
    size: {
        width: '800px',
        height: '400px'
    },
    // 限制顶部
    // tilt_up_max: Math.PI / 7,       
    // 限制底部
    // tilt_down_max: Math.PI / 7     
});

7.API选择, 配置参数介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
panorama:(必选)全景图的路径。
container:(必选)放置全景图的容器。
autoload:(默认为truetrue为自动加载全景图,false为迟点加载全景图(通.过load方法)。
usexmpdata:(默认值为true)photo sphere viewer是否必须读入xmp数据,false为不必须。
cors_anonymous:(默认值为truetrue为不能通过cookies获得用户
pano_size:(默认值为null)全景图的大小,是否裁切。
default_position:(默认值为0)定义默认位置,用户看见的第一个点,例如:{long: math.pi, lat: math.pi/2}。
min_fov:(默认值为30)观察的最小区域,单位degrees,在1-179之间。
max_fov:(默认值为90)观察的最大区域,单位degrees,在1-179之间。
allow_user_interactions:(默认值为true)设置为false,则禁止用户和全景图交互(导航条不可用)。
allow_scroll_to_zoom:(默认值为true)若设置为false,则用户不能通过鼠标滚动进行缩放图片。
tilt_up_max:(默认值为math.pi/2)向上倾斜的最大角度,单位radians。
tilt_down_max:(默认值为math.pi/2)向下倾斜的最大角度,单位radians。
min_longitude:(默认值为0)能够展示的最小经度。
max_longitude:(默认值为2PI)能够展示的最大维度。
zoome_level:(默认值为0)默认的缩放级别,值在0-100之间。
long_offset:(默认值为PI/360)mouse/touch移动时每像素经过的经度值。
lat_offset:(默认值为pi/180)mouse/touch移动时每像素经过的纬度值。
time_anim(默认值为2000)全景图在time_anim毫秒后会自动进行动画。(设置为false禁用它)
reverse_anim:(默认值为true)当水平方向到达最大/最小的经度时,动画方向是否反转(仅仅是不能看到完整的圆)。
anim_speed:(默认值为2rpm)动画每秒/分钟多少的速度。
vertical_anim_speed:(默认值为2rpm)垂直方向的动画每秒/分钟多少的速度。
vertical_anim_target:(默认值为0)当自动旋转时的维度,默认为赤道。
navbar:(默认为false)显示导航条。
navbar_style:(默认值为false)导航条的样式。有效的属性:
    backgroundColor:导航条背景色(默认值rgba(61, 61, 61, 0.5));
    buttonsColor:按钮前景色(默认值 rgba(255, 255, 255, 0.7));
    buttonBackgroundColor:按钮激活时的背景色(默认值 rgba(255, 255, 255, 0.1));
    buttonsHeight:按钮高度,单位px(默认值 20);
    autorotateThickness:自动旋转图片的层(默认值 1);
    zoomRangeWidth:缩放游标的宽度,单位px(默认值 50);
    zoomRangeThickness:缩放游标的层(默认值 1);
    zoomRangeDisk:缩放游标的放大率,单位px(默认值 7);
    fullscreenRatio:全屏图标的比例(默认值 4/3);
    fullscreenThickneee:全屏图片的层,单位px(默认值 2)
loading_msg:(默认值为Loading...)加载信息。
loading_img:(默认值 为null)loading图片的路径。
loading_html:(默认值 为null)html加载器(添加到容器中的元素或字符串)。
size:(默认值为null)全景图容器的最终尺寸,例如{width: 500, height: 300}。
onready:(默认值为null)全景图准备好并且第一张图片展示出来后的回调函数。
方法介绍
addAction():添加事件(插件没有提供执行事件的方法,似乎是提供给插件内部使用的)。
fitToContainer():调整全景图容器大小为指定大小。
getPosition():获取坐标经纬度。
getPositionInDegrees():获取经纬度度数。
getZoomLevel():获取缩放级别。
load():加载全景图()。
moveTo(longitude, latitude):根据经纬度移动到某一点。
rotate(dlong, dlat):根据经纬度度数移动到某一点。
toggleAutorotate():是否开启全景图自动旋转。
toggleDeviceOrientation():是否开启重力感应方向控制。
toggleFullscreen():是否开启全景图全屏。
toggleStereo():是否开启立体效果(可用于WebVR哦)。
zoom(level):设置缩放级别。
zoomIn():放大。
zoomOut():缩小。

示例参考:https://threejs.org/examples/#webgl_animation_cloth

 

 
Copyright © 2008-2021 lanxinbase.com Rights Reserved. | 粤ICP备14086738号-3 |