threejs创建3d交互地图
生活随笔
收集整理的這篇文章主要介紹了
threejs创建3d交互地图
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
文章目錄
- 前言
- 關鍵點
- 源碼
- 總結
前言
基于react-hooks創建的三維地圖,只實現了基本的交互展示,可根據個人喜好增加各種交互和展示效果,效果如下。
關鍵點
使用threejs創建3d地圖注意的組要是以下幾點。
- GeoJson數據規范,尤其是面狀Feature的數據結構特點,可參考官網:https://geojson.org/。
- 地圖生成和交互主要是使用THREE.ExtrudeBufferGeometry和THREE.Raycaster()方法。
- 中國政區GeoJSON數據可從阿里云數據平臺下載。將經緯度坐標轉移到屏幕上使用d3.js的geoMercator()方法d3官網。
源碼
完整代碼如下,附注釋。
import { useRef, useEffect, useCallback, useState } from 'react' import * as THREE from 'three' import * as D3 from 'd3' import OrbitControls from 'three-orbitcontrols'; import './index.scss'const View = () => {const page = useRef(); // useRef不會導致重新渲染/*** 場景、相機、渲染器作為threejs的基本結構,需要在頁面進入時渲染完畢*/const scence = useRef(new THREE.Scene()).current; //場景const camera = useRef(new THREE.PerspectiveCamera()).current; //攝像機(透視投影)const render = useRef(new THREE.WebGLRenderer()).current; //渲染器const controls = new OrbitControls(camera, render.domElement);//創建控件對象const timer = useRef(null) // 定義定時器const handleProj = D3.geoMercator().center([109, 34.5]).scale(80).translate([0, 0]) // d3投影轉換函數const mapContainer = useRef(new THREE.Object3D()).current; // 存儲地圖Object3D對象const lastPickedProvince = useRef(null) // 上次選中的省份useEffect(() => {page.current.appendChild(render.domElement);initScene();initLight();initGeom();renderScene();}, [])useEffect(() => {bindEvent();}, [])const bindEvent = () => {window.addEventListener('mousemove', (event) => {const pointer = new THREE.Vector2();// 像素坐標=>規范化設備坐標系 [-1,1]pointer.x = (event.clientX / window.innerWidth) * 2 - 1;pointer.y = - (event.clientY / window.innerHeight) * 2 + 1;// 獲取鼠標點擊的位置生成射線const raycaster = new THREE.Raycaster();raycaster.setFromCamera(pointer, camera);// 獲取射線相交的物體集合// debuggerconst intersects = raycaster.intersectObjects(mapContainer.children, true);if (intersects.length) {const pcickedProvice = intersects[0].object;// 選中了新的省份if (lastPickedProvince.current?.properties !== pcickedProvice.properties) {// 上次選中的恢復半透明if (lastPickedProvince.current) {lastPickedProvince.current.material.opacity = 0.5}pcickedProvice.material.opacity = 1; // 新選中的設置為不透明lastPickedProvince.current = pcickedProvice;}} else { // 鼠標移開地圖,之前選中的省份回復半透明if (lastPickedProvince.current) {lastPickedProvince.current.material.opacity = 0.5}lastPickedProvince.current = null;}}, false)}// 初始化場景const initScene = useCallback(() => {render.setSize(page.current.offsetWidth, page.current.offsetHeight); // 渲染器設置尺寸// 設置背景顏色render.setClearColor(new THREE.Color(0x000000)); // 設置背景顏色和透明度render.shadowMap.enabled = true; // 渲染器允許渲染陰影?/*** 設置攝像機的屬性*/camera.aspect = (page.current.offsetWidth / page.current.offsetHeight) // 攝像機設置屏幕寬高比camera.fov = 45; // 攝像機的視角camera.near = 0.01; // 近面距離camera.far = 1001; // 遠面距離camera.position.set(2, 2, 200) // 設置攝像機在threejs坐標系中的位置camera.lookAt(0, 0, 0) // 攝像機的指向camera.updateProjectionMatrix(); // 更新攝像機投影矩陣,在任何參數被改變以后必須被調用}, [render, scence])// 初始化環境光const initLight = () => {const ambLight = new THREE.AmbientLight('#ffffff', 0.3) // 基本光源/*** 設置聚光燈相關的的屬性,詳情見P54*/const spotLight = new THREE.SpotLight(0xFFFFFF); // 聚光燈spotLight.position.set(40, 200, 10);spotLight.castShadow = true; // 只有該屬性為true時,該點光源允許產生陰影,并且下列屬性可用scence.add(ambLight, spotLight); // 向場景中添加光源}// 初始化地理數據集const initGeom = () => {// 加載中國地區的geoJson數據集const fileLoader = new THREE.FileLoader();fileLoader.load('https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json',(data) => {const chinaJson = JSON.parse(data)handleData(chinaJson)})}// 處理GeoJson dataconst handleData = (jsonData) => {const feaureList = jsonData.features;feaureList.forEach((feature) => { // 每個feature都代表一個省份const province = new THREE.Object3D;province.properties = feature.properties.name // 省份名稱province.name = feature.properties.name // 省份名稱const coordinates = feature.geometry.coordinates // 省份坐標信息if (feature.geometry.type === 'MultiPolygon') {coordinates.forEach((coord) => {coord.forEach((coordinate) => {const extrudeMesh = creatDepthPolygon(coordinate)extrudeMesh.properties = feature.properties.nameprovince.add(extrudeMesh)})})}if (feature.geometry.type === 'Polygon') {coordinates.forEach((coordinate) => {const extrudeMesh = creatDepthPolygon(coordinate)extrudeMesh.properties = feature.properties.nameprovince.add(extrudeMesh)})}mapContainer.add(province)})scence.add(mapContainer)}// 創建三維多邊形const creatDepthPolygon = (coordinate) => {const shape = new THREE.Shape();coordinate.forEach((item, index) => { // 每一個item都是MultiPolygon中的一個polygonconst [x_XYZ, y_XYZ] = handleProj(item)if (index === 0) {shape.moveTo(x_XYZ, -y_XYZ)} else {shape.lineTo(x_XYZ, -y_XYZ)}})const geometry = new THREE.ExtrudeBufferGeometry(shape, { depth: 1, bevelEnabled: false, })const material = new THREE.MeshBasicMaterial({color: new THREE.Color(Math.random() * 0xffffff), // 每個省隨機賦色transparent: true,opacity: 0.5})return new THREE.Mesh(geometry, material)}// 渲染器執行渲染const renderScene = useCallback(() => {timer.current = window.requestAnimationFrame(() => renderScene())controls.update();render.render(scence, camera);}, [render])return (<><div className='page' ref={page} ><div style={{ position: 'fixed', top: '0', right: '0' }}><button onClick={() => { console.log(scence) }} style={{ marginRight: '10px' }}>打印場景</button></div></div></>) };export default View總結
three實現3d交互地圖的注意點
- GeoJson面狀Feature的數據結構
- 地圖生成和交互核心方法
- 原始數據來源
總結
以上是生活随笔為你收集整理的threejs创建3d交互地图的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python3表白代码弹窗_抖音整蛊表白
- 下一篇: 哈工大软件构造课程知识点总结(三)