小工具知识网

当前位置: 首页 > 行业知识 > three.js实现3d地图,包含散点,地图背景图片(vue)

行业知识

three.js实现3d地图,包含散点,地图背景图片(vue)

2026-11-07 05:47:26 来源:互联网转载

效果图

图片资源:

1、下载three.js及d3依赖,我这里使用的是three r153版本

npm i threenpm i d3

2、使用d3将地理坐标转换成坐标轴xy值,在地图区域及边界线加载到场景后,通过包围盒计算完整地图的max和min坐标,再根据max和min的值重新计算每个地图区域的uv坐标,如果不重新计算uv坐标会导致贴图显示异常

this.projection = d3        .geoMercator()        .center(this.centerCoordinate)        .translate([0, 0]);// 加载地图背景const backgroundTexture = new THREE.TextureLoader().load(  require("@/assets/images/map.png"));// 加载地图let fileLoader = new THREE.FileLoader();fileLoader.load("/anhui.json", (data) => {  // 添加地图及边界线  this.addMapGeometry(data);  // 重新计算地图uv坐标  let arr = [];  let box = new THREE.Box3();  for (let v of this.map.children) {    for (let v2 of v.children) {      // 判断是否为ExtrudeGeometry      if (v2.geometry instanceof THREE.ExtrudeGeometry) {        arr.push(v2);        let itemBox = new THREE.Box3().setFromObject(v);        box.union(itemBox);      }    }  }  var bboxMin = box.min;  var bboxMax = box.max;  // 计算UV的缩放比例  var uvScale = new THREE.Vector2(    1 / (bboxMax.x - bboxMin.x),    1 / (bboxMax.y - bboxMin.y)  );  for (let v of arr) {    let uvAttribute = v.geometry.getAttribute("uv");    for (let i = 0; i  {  // 定一个省份3D对象  const province = new THREE.Object3D();  // 每个的 坐标 数组  const coordinates = elem.geometry.coordinates;  if (elem.geometry.type === "MultiPolygon") {    // 循环坐标数组    coordinates.forEach((multiPolygon) => {      multiPolygon.forEach((polygon) => {        this.drawItem(elem, polygon, province);      });    });    this.map.add(province);  } else if (elem.geometry.type === "Polygon") {    // 循环坐标数组    coordinates.forEach((polygon) => {      this.drawItem(elem, polygon, province);    });    this.map.add(province);  }});this.scene.add(this.map);},drawItem(elem, polygon, province) {const shape = new THREE.Shape();const pointsArray = new Array();for (let i = 0; i  

3、设置后期处理

//设置光晕this.composer = new EffectComposer(this.renderer); //效果组合器//创建通道let renderScene = new RenderPass(this.scene, this.camera);this.composer.addPass(renderScene);let outlinePass = new OutlinePass(  new THREE.Vector2(window.innerWidth, window.innerHeight),  this.scene,  this.camera,  this.boundaryLineArr // 边界线数组);outlinePass.renderToScreen = true;outlinePass.edgeGlow = 2; // 光晕效果outlinePass.usePatternTexture = false;outlinePass.edgeThickness = 10; // 边框宽度outlinePass.edgeStrength = 1.5; // 光晕效果outlinePass.pulsePeriod = 0; // 光晕闪烁的速度outlinePass.visibleEdgeColor.set("#1acdec");outlinePass.hiddenEdgeColor.set("#1acdec");this.composer.addPass(outlinePass);

完整代码

注:我这边使用的是安徽的地图,需要替换自己的地图json以及地图中心地理坐标

            {{ selectedPointData.name }}      import * as THREE from "three";import * as d3 from "d3";import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass.js";import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass.js";import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";export default {  data() {    return {      scene: null,      camera: null,      renderer: null,      controls: null,      centerCoordinate: [117.13, 31.89], // 地图中心地理坐标      projection: null, // Mercator 投影      mapConfig: {        deep: 0.2, // 挤出的深度      },      boundaryLineArr: [], // 边界线      composer: "", // 后期处理      pointData: [        {          coordinates: [117.33, 31.79],          type: 1,          name: "合肥",          value: 100,        },        {          coordinates: [118.502, 31.684],          type: 1,          name: "马鞍山",          value: 100,        },      ],      pointInstanceArr: [], // 坐标点实例      show: false, // 是否显示tooltip      selectedPointData: {}, // 选中的坐标点数据    };  },  mounted() {    this.init();    window.addEventListener("resize", () => {      this.renderer.setSize(window.innerWidth, window.innerHeight);      this.camera.aspect = window.innerWidth / window.innerHeight;      this.camera.updateProjectionMatrix();    });  },  methods: {    init() {      this.renderer = new THREE.WebGLRenderer();      this.renderer.setSize(window.innerWidth, window.innerHeight);      this.renderer.outputColorSpace = THREE.LinearSRGBColorSpace;      document.querySelector("#page").appendChild(this.renderer.domElement);      this.scene = new THREE.Scene();      this.camera = new THREE.PerspectiveCamera(        45,        window.innerWidth / window.innerHeight,        0.1,        1000      );      this.camera.position.set(5, 5, 26);      this.camera.lookAt(0, 0, 0);      // let axesHelp = new THREE.AxesHelper(5);      // this.scene.add(axesHelp);      this.controls = new OrbitControls(this.camera, this.renderer.domElement);      // 墨卡托投影转换      this.projection = d3        .geoMercator()        .center(this.centerCoordinate)        .translate([0, 0]); // 根据地球贴图做轻微调整      // 添加地图      this.addMap();      // 给地图边界线添加outline效果      this.setLineOutline();      // 添加灯光      let ambientLight = new THREE.AmbientLight(0xffffff, 1);      this.scene.add(ambientLight);      // 添加散点      this.setPoint();      // 设置光线投射      this.setRaycaster();      this.render();    },    render() {      this.renderer.render(this.scene, this.camera);      this.controls.update();      if (this.composer) this.composer.render();      requestAnimationFrame(this.render);    },    // 添加地图    addMap() {      // 加载地图背景      const backgroundTexture = new THREE.TextureLoader().load(        require("@/assets/images/map.png")      );      // 加载地图      let fileLoader = new THREE.FileLoader();      fileLoader.load("/anhui.json", (data) => {        // 添加地图及边界线        this.addMapGeometry(data);        // 重新计算地图uv坐标        let arr = [];        let box = new THREE.Box3();        for (let v of this.map.children) {          for (let v2 of v.children) {            // 判断是否为ExtrudeGeometry,只计算所有地图区域总和的包围盒大小            if (v2.geometry instanceof THREE.ExtrudeGeometry) {              arr.push(v2);              let itemBox = new THREE.Box3().setFromObject(v2);              box.union(itemBox);            }          }        }        var bboxMin = box.min;        var bboxMax = box.max;        // 计算UV的缩放比例        var uvScale = new THREE.Vector2(          1 / (bboxMax.x - bboxMin.x),          1 / (bboxMax.y - bboxMin.y)        );        for (let v of arr) {          let uvAttribute = v.geometry.getAttribute("uv");          for (let i = 0; i  {        // 定一个省份3D对象        const province = new THREE.Object3D();        // 每个的 坐标 数组        const coordinates = elem.geometry.coordinates;        if (elem.geometry.type === "MultiPolygon") {          // 循环坐标数组          coordinates.forEach((multiPolygon) => {            multiPolygon.forEach((polygon) => {              this.drawItem(elem, polygon, province);            });          });          this.map.add(province);        } else if (elem.geometry.type === "Polygon") {          // 循环坐标数组          coordinates.forEach((polygon) => {            this.drawItem(elem, polygon, province);          });          this.map.add(province);        }      });      this.scene.add(this.map);    },    drawItem(elem, polygon, province) {      const shape = new THREE.Shape();      const pointsArray = new Array();      for (let i = 0; i  {        pointer.x = (event.clientX / window.innerWidth) * 2 - 1;        pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;        raycaster.setFromCamera(pointer, this.camera);        const intersects = raycaster.intersectObjects(this.pointInstanceArr);        if (intersects && intersects.length > 0) {          let tooltip = this.$refs.tooltip;          tooltip.style.left = event.pageX + "px";          tooltip.style.top = event.pageY + "px";          this.selectedPointData = intersects[0].object.properties;          this.show = true;        } else {          this.selectedPointData = {};          this.show = false;        }      });    },  },};.page {  height: 100vh;  .tooltip {    position: absolute;    background-color: #fff;    padding: 10px;    border-radius: 8px;  }}

上一篇:完美世界怎么选服务器号?

下一篇:揭秘CSharp源码世界,探寻编程魅力