Three.js是一个使用webGL创建3d图形的JavaScript库。主要用于3d图形可视化。本篇文章以创建3d地图的例子,展示three.js的使用。
技术栈:vue3+three.js
使用threejs生成3d地图分为3步
- 获取地图数据
- 使用threejs创建地图形状
- 渲染创建的形状
获取地图数据
地图数据可以通过datav网站获取,获取的文件是一个json文件。
使用threejs创建地图形状
地图json数据中会有一个geomerey属性,这个属性用于描述地理形状,这个属性的值是一个多维数组,数组中每个元素都是一组经纬度坐标,但是threejs不能直接解析经纬度坐标所以需要把经纬度坐标转换为平面坐标。我们可以使用d3.js库把经纬度转换为平面坐标。
创建场景、相机、渲染器
创建场景、相机、渲染器之前,需要创建一个容器元素,用来承载three.js创建的场景
<template>
<div style="width:100%;height:100%" ref="container"></div>
</template>
接下来开始使用js创建场景、相机、渲染器
// 引入需要的库
import * as THREE from 'three'
import {onMounted} from 'vue'
import sichuanJson from '@/assets/map/sichuan.json?raw'
import * as D3 from 'd3'
const container = ref()
// 创建场景
const scene = new THREE.Scene();
// 创建渲染器
const renderer = new THREE.WebGLRenderer({
antialias: true,
});
renderer.setSize(window.innerWidth, window.innerHeight);
// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000);
camera.position.set(-50,50,130);
onMounted(()=>{
// 把threejs渲染器创建的元素添加到容器元素中
container.value!.appendChild(renderer.domElement);
});
// 渲染添加到场景中的物体
function render(){
renderer.render(scene,camera);
requestAnimationFrame(render);
}
场景、相机、渲染器目前创建完毕,接下来创建地图形状
根据geometry属性创建形状
// 声明一个函数用于创建地图形状
function createSichuanShape(){
// 解析json数据
const jsonData = JSON.parse(sichuanJson);
// 创建一个分组,把地图中的多个图形放在一个组中,便于管理
const sichuan = new THREE.Group();
// 使用d3把经纬度转换成平面坐标
const projection = D3.geoMercator().center([104.065735,30.659462])
// 遍历json数据中的features
jsonData.features.forEach((feature,i)=>{
// 获取feature中的geometry数据,方便引用
const geometry = feature.geometry;
const type = geometry.type;
// 创建分组,把同一个市的形状放在一个分组,便于管理
const city = new THREE.Group();
if(type === 'MultiPolygon'){
// 遍历geometry中的数据
geometry.coordinates.forEach((multipolygon)=>{
// 创建形状,用于展示地理形状
const shape = new THREE.Shape()
// 存储每个坐标,用于绘制边界线
const arr = []
multipolygon.forEach((polygon)=>{
polygon.forEach((item,index) => {
// 使用d3转换坐标
const [x,y]= projection.translate([0,0])(item);
// 根据转换后的坐标绘制形状
if(index === 0){
shape.moveTo(x,y)
}else {
shape.lineTo(x,y)
}
arr.push(x,y,3)
});
})
// 绘制边界线
createLine(arr,city)
// 创建 ExtrudeGeometry用于显示3d效果
const extrudGeometry = new THREE.ExtrudeGeometry(shape,{
depth:2 // 地图的厚度
})
// 创建材质,用于显示地图的外观
const material = new THREE.MeshBasicMaterial({
color:'#ffff33'
});
// 创建物体,用于显示地图
const mesh = new THREE.Mesh(extrudGeometry,material)
mesh.position.set(0,0,0)
// 添加到分组
city.add(mesh)
})
}
// 添加到分组
sichuan.add(city)
})
// 添加到场景
scene.add(sichuan)
}
// 用于创建边界线
function createLine(points,container){
const buffGeometry = new THREE.BufferGeometry();
buffGeometry.setAttribute('position',new THREE.BufferAttribute(new Float32Array(points),3))
const lineMaterial = new THREE.LineBasicMaterial({
color:0xff0000
})
const line = new THREE.Line(buffGeometry,lineMaterial)
container.add(line)
}
通过上面的操作我们已经把3d地图绘制出来,效果图: