先上效果圖
需要查找地圖的josn數(shù)據(jù)、需要引入的js、 和需要安裝的依賴如下
1.先獲取想要展示地圖的區(qū)域json數(shù)據(jù)
阿里云獲取地圖區(qū)域的json
示例為陜西省
2.npm安裝three.js和d3 npm i three
、 npm i d3
3.引入相關(guān)方法和json數(shù)據(jù)文章來源:http://www.zghlxwxcb.cn/news/detail-851581.html
import * as THREE from 'three';
import { OrbitControls } from "../../../node_modules/three/examples/jsm/controls/OrbitControls.js"
import { TextGeometry } from '../../../node_modules/three/examples/jsm/geometries/TextGeometry.js';
import { FontLoader } from '../../../node_modules/three/examples/jsm/loaders/FontLoader.js';
import * as d3 from 'd3';
import jsondata from './shanxi.json'
具體代碼
<template>
<div class="center-map-box" id="contant">
</div>
</template>
mounted() {
// 第一步新建一個場景
this.scene = new THREE.Scene()
this.contant = document.getElementById('contant')
// 輔助線
// const axesHelper = new THREE.AxesHelper(10);
// this.scene.add(axesHelper);
// 光源
this.spotLight = new THREE.PointLight('#fff', 4, 100)
this.spotLight.position.set(0.2, -0.4, 1)
this.scene.add(this.spotLight)
//環(huán)境光
const ambient = new THREE.AmbientLight('#fff', 4)
this.scene.add(ambient)
// 可視化點(diǎn)光源
// const pointLightHelper = new THREE.PointLightHelper(this.spotLight, 0.1)
// this.scene.add(pointLightHelper)
this.setCamera()
this.setRenderer()
this.generateGeometry()
this.setClickFn()
this.setController()
this.animate()
window.onresize = () => {
this.renderer.setSize(this.contant.clientWidth, this.contant.clientHeight);
this.camera.aspect = this.contant.clientWidth / this.contant.clientHeight;
this.camera.updateProjectionMatrix();
};
}
methods: {
// 新建透視相機(jī)
setCamera() {
this.camera = new THREE.PerspectiveCamera(60, this.contant.clientWidth / this.contant.clientHeight, 0.1, 500);
this.camera.position.z = 10
},
// 設(shè)置渲染器
setRenderer() {
this.renderer = new THREE.WebGLRenderer()
// 設(shè)置畫布的大小
this.renderer.setSize(this.contant.clientWidth, this.contant.clientHeight)
//這里 其實(shí)就是canvas 畫布 renderer.domElement
this.contant.appendChild(this.renderer.domElement)
this.renderer.setClearColor(0x000000, 0)
},
render() {
this.renderer.render(this.scene, this.camera)
},
generateGeometry() {
// 初始化一個地圖對象
this.map = new THREE.Object3D()
// 墨卡托投影轉(zhuǎn)換
const projection = d3
.geoMercator()
.center([104.0, 37.5])
.scale(80)
.translate([0, 0])
jsondata.features.forEach((elem) => {
this.renderer.render(this.scene, this.camera);
const coordinates = elem.geometry.coordinates
// 循環(huán)坐標(biāo)數(shù)組
coordinates.forEach((multiPolygon) => {
multiPolygon.forEach((polygon, index) => {
const province = new THREE.Object3D()
const shape = new THREE.Shape()
const lineMaterial = new THREE.LineBasicMaterial({
color: 'white',
})
const lineGeometry = new THREE.BufferGeometry()
const pointsArray = new Array()
for (let i = 0; i < polygon.length; i++) {
const [x, y] = projection(polygon[i])
if (i === 0) {
shape.moveTo(x, -y)
}
shape.lineTo(x, -y)
pointsArray.push(new THREE.Vector3(x, -y, 0))
}
lineGeometry.setFromPoints(pointsArray)
const extrudeSettings = {
depth: 0.07,
bevelEnabled: false,
}
const geometry = new THREE.ExtrudeGeometry(
shape,
extrudeSettings
)
const material = new THREE.MeshPhongMaterial({
color: '#43A7FF',
transparent: true,
opacity: 0.8,
})
const material1 = new THREE.MeshBasicMaterial({
color: '#3480C4',
transparent: true,
opacity: 0.4,
})
const loader = new FontLoader();
//字體需放到根目錄public下
loader.load('./fonts/FZCuHeiSongS-B-GB_Regular.json', (font) => {
const fontOption = {
font: font,
size: 0.07,
height: 0.01,
curveSegments: 1,
bevelThickness: 1,
bevelSize: 0,
bevelEnabled: false,
bevelSegments: 0
};
const txtMater = new THREE.MeshBasicMaterial({ color: 0xffffff });
const txtGeometry = new TextGeometry(name, fontOption);
const txtMesh = new THREE.Mesh(txtGeometry, txtMater);
const [x, y] = projection(elem.properties.center)
txtMesh.position.set(x - 8.3, -y + 4.4, 0.08)
if (name == 'xx縣') {
//這里位置不對可以做微調(diào)
txtMesh.position.set(x - 8.33, -y + 4.55, 0.08)
}
this.scene.add(txtMesh);
});
var name = elem.properties.name;//區(qū)縣名
const mesh = new THREE.Mesh(geometry, [material, material1])
const line = new THREE.Line(lineGeometry, lineMaterial)
this.materialArr.push(material)
province.properties = elem.properties
province.add(mesh)
province.add(line)
this.map.add(province)
this.render()
})
})
})
this.map.position.set(-8.2, 4.4, 0);
this.scene.add(this.map);
this.spotLight.target = this.map;
this.camera.position.set(0, -0.7, 2.5);
this.renderer.render(this.scene, this.camera);
},
//加事件
setClickFn() {
this.raycaster = new THREE.Raycaster();
this.mouse = new THREE.Vector2();
const onMouseMove = (event) => {
var marginLeft = this.contant.offsetLeft
var marginTop = this.contant.offsetTop + 92
// 如果該地圖不是占滿全屏需要減去margintop和marginleft
// 將鼠標(biāo)位置歸一化為設(shè)備坐標(biāo)。x 和 y 方向的取值范圍是 (-1 to +1)
// this.mouse.x = (event.clientX / this.contant.clientWidth) * 2 - 1;
// this.mouse.y = -(event.clientY / this.contant.clientHeight) * 2 + 1;
this.mouse.x = ((event.clientX - marginLeft) / this.contant.clientWidth) * 2 - 1;
this.mouse.y = -((event.clientY - marginTop) / this.contant.clientHeight) * 2 + 1;
};
let clickPosition;
window.addEventListener("mousemove", onMouseMove, false);
const onclick = (event) => {
var marginLeft = this.contant.offsetLeft
var marginTop = this.contant.offsetTop
// let x = (event.clientX / this.contant.clientWidth) * 2 - 1;
// let y = -(event.clientY / this.contant.clientHeight) * 2 + 1;
// 如果該地圖不是占滿全屏需要減去margintop和marginleft
let x = ((event.clientX - marginLeft) / this.contant.clientWidth) * 2 - 1;
let y = -((event.clientY - marginTop) / this.contant.clientHeight) * 2 + 1;
clickPosition = { x: x, y: y };
this.raycaster.setFromCamera(clickPosition, this.camera);
// 算出射線 與當(dāng)場景相交的對象有那些
const intersects = this.raycaster.intersectObjects(this.scene.children, true);
let clickObj = intersects.find(
(item) => item.object.material && item.object.material.length === 2
);
// 點(diǎn)擊區(qū)縣
if (clickObj && clickObj.object) {
console.log(clickObj)
// this.$emit('clickAreaCheck',clickObj)
}
};
window.addEventListener("mousedown", onclick, false);
},
// 設(shè)置最大旋轉(zhuǎn)的角度
setController() {
const controls = new OrbitControls(this.camera, this.renderer.domElement);
controls.maxPolarAngle = 2.5
controls.minPolarAngle = 1
controls.maxAzimuthAngle = 1
controls.minAzimuthAngle = -1
controls.addEventListener("change", () => {
this.renderer.render(this.scene, this.camera);
});
},
animate() {
window.requestAnimationFrame(this.animate);
this.raycaster.setFromCamera(this.mouse, this.camera);
this.renderer.render(this.scene, this.camera);
},
}
該文章只做記錄,具體在場景中使用中自己調(diào)整。文章來源地址http://www.zghlxwxcb.cn/news/detail-851581.html
到了這里,關(guān)于threejs+vue 省份3D可視化地圖的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!