源码资料获取:https://github.com/huangchun0121/3D-example/tree/main/省份立体板块
实现代码:
点击查看代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>中国地图</title>
<style>
/*body{*/
/* width: 100%;*/
/* height: 100%;*/
/* padding: 0;*/
/* margin: 0;*/
/*}*/
</style>
</head>
<body>
<div id="provinceInfo"></div>
<script src="js/three.js"></script>
<script src="js/three.orbitcontrols.js"></script>
<script src="js/d3-array.v1.min.js"></script>
<script src="js/d3-geo.v1.min.js"></script>
<script>
let renderer, camera, scene, light, controller;
const width = window.innerWidth, height = window.innerHeight;
var map;
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
/**
* @description 初始化渲染场景
*/
function initRenderer() {
renderer = new THREE.WebGLRenderer({attributes:true});
renderer.setSize(width,height);
document.body.appendChild(renderer.domElement);
}
/**
* @description 初始化相机
*/
function initCamera() {
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, -70, 150);
camera.lookAt(0, 0, 0);
}
/**
* @description 初始化场景
*/
function initScene() {
scene = new THREE.Scene();
}
/**
* 初始化用户交互
**/
function initControls() {
controller = new THREE.OrbitControls(camera, renderer.domElement);
}
/**
* 初始化物体
*/
function initObject(){
var loader = new THREE.FileLoader();
loader.load('model/chinaJson.json',function(data){
var chinaData = JSON.parse(data);
initMap(chinaData);
});
window.addEventListener('mousemove', onm ouseMove, false);
}
/**
* 描绘中国边界(平面)
*/
function initMap(chinaJson) {
// 建一个空对象存放对象
map = new THREE.Object3D();
// 墨卡托投影转换
const projection = d3.geoMercator().center([104.0, 37.5]).scale(80).translate([0, 0]);
chinaJson.features.forEach(elem => {
// 定一个省份3D对象
const province = new THREE.Object3D();
// 每个的 坐标 数组
const coordinates = elem.geometry.coordinates;
// 循环坐标数组
coordinates.forEach(multiPolygon => {
multiPolygon.forEach(polygon => {
const shape = new THREE.Shape();
const lineMaterial = new THREE.LineBasicMaterial({
color: 'white'
});
const lineGeometry = new THREE.Geometry();
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);
lineGeometry.vertices.push(new THREE.Vector3(x, -y, 4.01));
}
const extrudeSettings = {
depth: 4,
bevelEnabled: false
};
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
const material = new THREE.MeshBasicMaterial({
color: '#02A1E2',
transparent: true,
opacity: 0.6
});
const material1 = new THREE.MeshBasicMaterial({
color: '#3480C4',
transparent: true,
opacity: 0.5
});
/* const material = new THREE.MeshBasicMaterial({ color: '#dedede', transparent: false, opacity: 0.6 });
const material1 = new THREE.MeshBasicMaterial({ color: '#dedede', transparent: false, opacity: 0.5 }); */
const mesh = new THREE.Mesh(geometry, [material, material1]);
const line = new THREE.Line(lineGeometry, lineMaterial);
province.add(mesh);
province.add(line)
})
})
// 将geo的属性放到省份模型中
province.properties = elem.properties;
if (elem.properties.contorid) {
const [x, y] = projection(elem.properties.contorid);
province.properties._centroid = [x, y];
}
map.add(province);
})
scene.add(map);
}
/**
* 鼠标拾取
*/
function onm ouseMove(event) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
/**
* @description 初始化光
*/
function initLight() {
const ambientLight = new THREE.AmbientLight( 0xffffff );
scene.add( ambientLight );
}
/**
* 窗口变动
**/
function onWindowResize() {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( innerWidth, innerHeight );
renders();
}
/**
* 渲染
*/
function renders() {
renderer.clear();
renderer.render( scene, camera );
}
/**
* 更新
**/
var activeInstersect =[];//用于存放选中的物体
function animate() {
requestAnimationFrame(animate);
if(controller) controller.update();
raycaster.setFromCamera(mouse,camera);
var intersects = raycaster.intersectObjects(scene.children,true);//获取鼠标与屏幕射线相交的物体
if (activeInstersect.length > 0) { // 将上一次选中的恢复好颜色
activeInstersect.forEach(element => {
element.object.material[0].color.set('#02A1E2');
element.object.material[1].color.set('#3480C4');
});
}
activeInstersect = []; // 设置为空
for (var i = 0; i < intersects.length; i++) { //
if (intersects[i].object.material && intersects[i].object.material.length === 2) {
activeInstersect.push(intersects[i]);
intersects[i].object.material[0].color.set(0xff0000);
intersects[i].object.material[1].color.set(0xff0000);
break; // 只取第一个
}
}
renders();
}
window.onload = () => {
initRenderer();
initCamera();
initScene();
initObject();
initLight();
initControls();
animate();
window.addEventListener('resize', onWindowResize, false);
}
</script>
</body>
</html>