Vue3 中实现 3D 地图——基于 Three.js 的实战教程

在现代 Web 开发中,3D 地图成为了展示数据和地理信息的一种新颖方式。借助 Three.js 和 Vue3,我们可以快速搭建一个 3D 地图应用。本教程将带你逐步实现 Vue3 与 Three.js 的集成,并构建一个简单的 3D 地图场景。

一、项目准备

1. 初始化 Vue3 项目

首先,创建一个 Vue3 项目。使用 Vue CLI 来快速初始化项目:

vue create vue-3d-map
cd vue-3d-map

2. 安装 Three.js 依赖

在项目中安装 Three.js:

npm install three

3. 项目结构概览

我们的项目结构大致如下:

vue-3d-map
│
├── public
│   ├── index.html
│
├── src
│   ├── components
│   │   └── Map3D.vue
│   ├── App.vue
│   └── main.js
│
└── package.json

二、创建 Map3D 组件

src/components/ 目录下创建 Map3D.vue 组件,作为我们 3D 地图的核心组件。

<template>
  <div ref="mapContainer" class="map-container"></div>
</template>

<script>
import * as THREE from 'three';

export default {
  name: "Map3D",
  mounted() {
    this.initScene();
  },
  methods: {
    initScene() {
      // 创建场景
      this.scene = new THREE.Scene();

      // 设置相机
      const width = this.$refs.mapContainer.clientWidth;
      const height = this.$refs.mapContainer.clientHeight;
      this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
      this.camera.position.set(0, 10, 30);

      // 创建渲染器
      this.renderer = new THREE.WebGLRenderer();
      this.renderer.setSize(width, height);
      this.$refs.mapContainer.appendChild(this.renderer.domElement);

      // 创建光源
      const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
      this.scene.add(ambientLight);

      const pointLight = new THREE.PointLight(0xffffff, 1);
      pointLight.position.set(10, 20, 10);
      this.scene.add(pointLight);

      // 创建地图基础立方体
      this.createBaseCube();

      // 渲染场景
      this.renderScene();
    },
    createBaseCube() {
      const geometry = new THREE.BoxGeometry(10, 1, 10);
      const material = new THREE.MeshStandardMaterial({ color: 0x4CAF50 });
      const cube = new THREE.Mesh(geometry, material);
      this.scene.add(cube);
    },
    renderScene() {
      this.renderer.render(this.scene, this.camera);
      requestAnimationFrame(this.renderScene);
    }
  }
};
</script>

<style>
.map-container {
  width: 100%;
  height: 100vh;
  overflow: hidden;
}
</style>

代码解析

  • 初始化场景:我们使用 THREE.Scene() 创建一个 Three.js 场景,并配置相机 PerspectiveCamera
  • 创建光源:添加环境光 AmbientLight 和点光源 PointLight,使得物体更加清晰。
  • 创建基础立方体:用 BoxGeometry 创建一个立方体来模拟地图的底层。
  • 渲染循环:通过 requestAnimationFrame 实现持续渲染。

三、在主页面中引入 3D 地图

App.vue 中引入 Map3D 组件:

<template>
  <div id="app">
    <Map3D />
  </div>
</template>

<script>
import Map3D from './components/Map3D.vue';

export default {
  name: 'App',
  components: {
    Map3D
  }
};
</script>

运行项目

启动项目,应该可以看到一个简单的 3D 地图场景:

npm run serve

四、丰富地图元素

现在,我们可以添加更多的地图元素,如山峰、水体等,以模拟真实地理环境。

1. 添加地形(平面网格)

createBaseCube 函数中,加入一个简单的平面网格:

createBaseCube() {
    
    
  const geometry = new THREE.PlaneGeometry(50, 50);
  const material = new THREE.MeshStandardMaterial({
    
     color: 0x8FBC8F });
  const plane = new THREE.Mesh(geometry, material);
  plane.rotation.x = -Math.PI / 2; // 平面朝上
  this.scene.add(plane);
}

2. 添加山峰

使用随机生成的锥体来模拟山峰:

addMountain() {
    
    
  for (let i = 0; i < 5; i++) {
    
    
    const geometry = new THREE.ConeGeometry(1 + Math.random() * 3, 5 + Math.random() * 10, 32);
    const material = new THREE.MeshStandardMaterial({
    
     color: 0x8B4513 });
    const mountain = new THREE.Mesh(geometry, material);

    mountain.position.set(
      Math.random() * 30 - 15,
      5,
      Math.random() * 30 - 15
    );

    this.scene.add(mountain);
  }
}

调用 addMountain 来生成随机的山峰:

this.addMountain();

3. 添加水体(透明平面)

addWaterBody() {
    
    
  const geometry = new THREE.PlaneGeometry(15, 15);
  const material = new THREE.MeshStandardMaterial({
    
    
    color: 0x1E90FF,
    transparent: true,
    opacity: 0.6
  });

  const water = new THREE.Mesh(geometry, material);
  water.position.y = 0.1;
  water.rotation.x = -Math.PI / 2;
  this.scene.add(water);
}

在初始化中调用 addWaterBody

this.addWaterBody();

五、相机控制和交互

为实现相机控制,我们可以引入 OrbitControls,让用户自由旋转和缩放视角。

npm install three/examples/jsm/controls/OrbitControls

然后在 Map3D.vue 中添加 OrbitControls

import {
    
     OrbitControls } from 'three/examples/jsm/controls/OrbitControls';

initScene() {
    
    
  // 创建相机和渲染器
  ...
  
  // 添加相机控制
  this.controls = new OrbitControls(this.camera, this.renderer.domElement);
  this.controls.enableDamping = true;
}

renderScene 中更新控制器:

renderScene() {
    
    
  this.controls.update();
  this.renderer.render(this.scene, this.camera);
  requestAnimationFrame(this.renderScene);
}

六、完整代码

以下是包含相机控制的完整代码:

<template>
  <div ref="mapContainer" class="map-container"></div>
</template>

<script>
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';

export default {
  name: "Map3D",
  mounted() {
    this.initScene();
  },
  methods: {
    initScene() {
      this.scene = new THREE.Scene();
      const width = this.$refs.mapContainer.clientWidth;
      const height = this.$refs.mapContainer.clientHeight;
      this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
      this.camera.position.set(0, 10, 30);

      this.renderer = new THREE.WebGLRenderer();
      this.renderer.setSize(width, height);
      this.$refs.mapContainer.appendChild(this.renderer.domElement);

      const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
      this.scene.add(ambientLight);

      const pointLight = new THREE.PointLight(0xffffff, 1);
      pointLight.position.set(10, 20, 10);
      this.scene.add(pointLight);

      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.enableDamping = true;

      this.createBaseCube();
      this.addMountain();
      this.addWaterBody();
      this.renderScene();
    },
    createBaseCube() {
      const geometry = new THREE.PlaneGeometry(50, 50);
      const material = new THREE.MeshStandardMaterial({ color: 0x8FBC8F });
      const plane = new THREE.Mesh(geometry, material);
      plane.rotation.x = -Math.PI / 2;
      this.scene.add(plane);
    },
    addMountain() {
      for (let i = 0; i < 5; i++) {
        const geometry = new THREE.ConeGeometry(1 + Math.random() * 3, 5 + Math.random() * 10, 32);
        const material = new THREE.Mesh

StandardMaterial({ color: 0x8B4513 });
        const mountain = new THREE.Mesh(geometry, material);

        mountain.position.set(
          Math.random() * 30 - 15,
          5,
          Math.random() * 30 - 15
        );

        this.scene.add(mountain);
      }
    },
    addWaterBody() {
      const geometry = new THREE.PlaneGeometry(15, 15);
      const material = new THREE.MeshStandardMaterial({
        color: 0x1E90FF,
        transparent: true,
        opacity: 0.6
      });

      const water = new THREE.Mesh(geometry, material);
      water.position.y = 0.1;
      water.rotation.x = -Math.PI / 2;
      this.scene.add(water);
    },
    renderScene() {
      this.controls.update();
      this.renderer.render(this.scene, this.camera);
      requestAnimationFrame(this.renderScene);
    }
  }
};
</script>

<style>
.map-container {
  width: 100%;
  height: 100vh;
  overflow: hidden;
}
</style>

总结

通过本教程,我们成功实现了在 Vue3 中使用 Three.js 构建 3D 地图的基本步骤。在此基础上,可以继续扩展地图的细节,如标记点、3D 模型导入等。希望这篇文章帮助你入门 Vue 和 Three.js 的组合开发。

猜你喜欢

转载自blog.csdn.net/qq_42978535/article/details/143573812