threejs 生成3d中国地图

1,059 阅读1分钟

threejs生成3d中国地图

threejs-3dmap.jpg

china.gif `import React, { useState, useEffect } from 'react' import * as THREE from 'three'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' import styles from './index.less' import * as d3 from 'd3' import china from './json/china.json'

export default function index() { let activeInstersect = []; // 设置为空 let provinceInfo = null const [container, setContainer] = useState(document.body) const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); // 渲染器 const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); container.appendChild(renderer.domElement);

// 场景
const scene = new THREE.Scene();

// 相机 透视相机
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, -70, 150);
camera.lookAt(0, 0, 0);




useEffect(() => {
    provinceInfo = document.getElementById('provinceInfo');
    setController(); // 设置控制
    setLight(); // 设置灯光
    setRaycaster();
    animate();
    initMap(china)
    // this.loadFont(); // 加载字体
    // loadMapData();
    setResize(); // 绑定浏览器缩放事件
}, [])
const setResize = () => {
    window.addEventListener('resize', function () {
        renderer.setSize(window.innerWidth, window.innerHeight);
    })
}
const initMap = (chinaJson) => {
    // 建一个空对象存放对象
    const 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();
        // province.name = elem.name;
        // 每个的 坐标 数组
        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.BufferGeometry()
                const points = []

                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);
                    points.push(new THREE.Vector3(x, -y, 4.01))
                }
                lineGeometry.setFromPoints(points);
                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 mesh = new THREE.Mesh(geometry, [material, material1]);
                // mesh.name = polygon.name
                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);
}


const setRaycaster = () => {
    const eventOffset = {};
    function onMouseMove(event) {
        mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
        eventOffset.x = event.clientX;
        eventOffset.y = event.clientY;
        provinceInfo.style.left = eventOffset.x + 2 + 'px';
        provinceInfo.style.top = eventOffset.y + 2 + 'px';
    }
    window.addEventListener('mousemove', onMouseMove, false);
}

const setLight = () => {
    let ambientLight = new THREE.AmbientLight(0xffffff); // 环境光
    scene.add(ambientLight);
}

const setController = () => {
    const controller = new OrbitControls(camera, renderer.domElement);
    scene.add(controller);
    /* this.controller.enablePan = false; // 禁止右键拖拽
    this.controller.enableZoom = true; // false-禁止右键缩放
    this.controller.maxDistance = 200; // 最大缩放 适用于 PerspectiveCamera
    this.controller.minDistance = 50; // 最大缩放
    this.controller.enableRotate = true; // false-禁止旋转 */
    /* this.controller.minZoom = 0.5; // 最小缩放 适用于OrthographicCamera
    this.controller.maxZoom = 2; // 最大缩放 */

}

const animate = () => {
    requestAnimationFrame(animate);
    raycaster.setFromCamera(mouse, camera);
    // 
    var intersects = raycaster.intersectObjects(scene.children, true);
    if (activeInstersect && 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; // 只取第一个
        }
    }
    const createProvinceInfo = () => { // 显示省份的信息      
        if (activeInstersect.length !== 0 && activeInstersect[0].object.parent.properties.name) {
            var properties = activeInstersect[0].object.parent.properties;
            provinceInfo.textContent = properties.name;
            provinceInfo.name = properties.name;
            provinceInfo.style.visibility = 'visible';
        } else {
            provinceInfo.style.visibility = 'hidden';
        }
    }
    createProvinceInfo();  //省份名字的显示隐藏
    renderer.render(scene, camera);
}
return (
    <div className={styles.provinceInfo} id='provinceInfo'>
    </div>
)

}

`