three0.143.0 三地地图展示记录

74 阅读2分钟
new chinaMap("#three");

import * as THREE from "three"; import * as d3 from "d3"; import { SVGLoader } from "three/examples/jsm/loaders/SVGLoader"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; import { FontLoader } from "three/examples/jsm/loaders/FontLoader"; import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry"; import fonts from "@/assets/Microsoft YaHei_Regular.json"; import svg from "@/assets/svg.svg"; import bottomImg from "@/assets/test.png"; import bian_ from "@/assets/bian_.png"; import bg_ from "@/assets/background.png";

import jsonData from "@/assets/哈尔滨市.json";

class chinaMap { constructor(id) { this.id = id; this.canvasBox = document.querySelector(id); this.width = this.canvasBox.offsetWidth; this.height = this.canvasBox.offsetHeight; this.init(); }

init() { //第一步新建一个场景 const bgt = new THREE.TextureLoader(); const bgt_w = bgt.load(bg_); this.scene = new THREE.Scene(); // this.scene.environment = bgt_w this.setRenderer(); this.setCamera(); this.setController();

this.setRaycaster();
this.generateGeometry(jsonData);
this.animate();
// this.addHelper();

}

// 设置渲染器 setRenderer() { this.renderer = new THREE.WebGLRenderer(); // 设置画布的大小 this.renderer.setSize(this.width, this.height); //这里 其实就是canvas 画布 renderer.domElement this.canvasBox.appendChild(this.renderer.domElement); }

// 设置环境光 setLight() { this.ambientLight = new THREE.AmbientLight(0xffffff); // 环境光 this.scene.add(ambientLight); } //render 方法 render() { this.renderer.render(this.scene, this.camera); } // 新建透视相机 setCamera() { this.camera = new THREE.PerspectiveCamera(40, this.width / this.height); this.camera.position.set(0, -20, 10); this.camera.lookAt(new THREE.Vector3(0, 0, 0)); } generateGeometry(jsondata) { this.huanmesh = []; // 初始化一个地图对象 this.map = new THREE.Object3D(); // 墨卡托投影转换 const projection = d3.geoMercator().center([128, 45.6]).translate([0, 0]); jsondata.features.forEach((elem) => { // 定一个省份3D对象 const province = new THREE.Object3D(); // 每个的 坐标 数组 const coordinates = elem.geometry.coordinates; // 循环坐标数组 //centroid let [cx, cy] = projection(elem.properties.center); let cp = new THREE.Vector3(cx, -cy); this.setFonts(elem.properties.name, cp, 0.9); this.svgToThree(30, svg, cp, 1); this.ripple(cp, 1); coordinates.forEach((multiPolygon) => { multiPolygon.forEach((polygon) => { const shape = new THREE.Shape(); const lineMaterial = new THREE.LineBasicMaterial({ color: "#80FF89", }); 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, 1)); } lineGeometry.setFromPoints(pointsArray); const extrudeSettings = { depth: 2, bevelEnabled: false, };

      const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);

      const textureLoader = new THREE.TextureLoader();
      const doorColorTexture = textureLoader.load(bottomImg);

      const material = new THREE.MeshBasicMaterial({
        map: doorColorTexture,
        color: "#708E6D",
        transparent: true,
        opacity: 1,
      });

      const textureLoader_ = new THREE.TextureLoader();
      const doorColorTexture_ = textureLoader_.load(bian_);
      const material1 = new THREE.MeshBasicMaterial({
        map: doorColorTexture_,
        color: "#486D30",
        transparent: true,
        opacity: 1,
      });

      const mesh = new THREE.Mesh(geometry, [material, material1]);
      const line = new THREE.Line(lineGeometry, lineMaterial);
      line.translateZ(1.001);
      province.properties = elem.properties;
      province.add(mesh);
      province.add(line);
    });
  });
  this.map.add(province);
});
this.map.translateZ(-1);
this.cityWaveAnimate();
this.scene.add(this.map);

}

//相机辅助线 addHelper() { const helper = new THREE.CameraHelper(this.camera); this.scene.add(helper); const size = 20; const divisions = 20; const gridHelper = new THREE.GridHelper(size, divisions); this.scene.add(gridHelper); } setController() { this.controller = new OrbitControls(this.camera, this.renderer.domElement); this.controller.update(); } //动画更新相机 animate() { requestAnimationFrame(this.animate.bind(this)); this.scene.rotation.z += 0.01;

this.controller.update();
// 通过摄像机和鼠标位置更新射线
this.raycaster.setFromCamera(this.mouse, this.camera);
// 算出射线 与当场景相交的对象有那些
const intersects = this.raycaster.intersectObjects(this.scene.children);
// 恢复上一次清空的
if (this.lastPick) {
  this.lastPick.object.material[0].color.set("#708E6D");
  this.lastPick.object.material[1].color.set("#486D30");
}
this.lastPick = null;
this.lastPick = intersects.find(
  (item) => item.object.material && item.object.material.length === 2
);
if (this.lastPick) {
  this.lastPick.object.material[0].color.set("#708E6D");
  this.lastPick.object.material[1].color.set("#486D30");
}

this.render();

} setRaycaster() { this.raycaster = new THREE.Raycaster(); this.mouse = new THREE.Vector2(); const onMouseMove = (event) => { // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1) this.mouse.x = (event.clientX / this.width) * 2 - 1; this.mouse.y = -(event.clientY / this.height) * 2 + 1; }; this.canvasBox.addEventListener("mousemove", onMouseMove, false); } // 字体 setFonts(text, pointArr, z) { const loader = new FontLoader(); let font = loader.parse(fonts); const geometry = new TextGeometry(text, { font: font, size: 0.3, height: 0.1, }); const material = new THREE.MeshBasicMaterial({ color: 0x49ef4 }); const mesh = new THREE.Mesh(geometry, material); mesh.position.set(pointArr.x, pointArr.y + 0.07, z + 0.6); mesh.rotateX(-80); this.fontmesh = mesh; this.scene.add(mesh); } // svg转3d svgToThree(extrusion, svg, pointInfo, z) { const fillMaterial = new THREE.MeshBasicMaterial({ color: "#CC8D00" }); const stokeMaterial = new THREE.LineBasicMaterial({ color: "#F8D99B", }); const loader = new SVGLoader(); const svgGroup = new THREE.Group();

loader.load(svg, (res) => {
  const svgData = res;
  svgData.paths.forEach((path) => {
    const shapes = SVGLoader.createShapes(path);
    shapes.forEach((shape) => {
      const meshGeometry = new THREE.ExtrudeGeometry(shape, {
        depth: extrusion,
        bevelEnabled: false,
      });
      const linesGeometry = new THREE.EdgesGeometry(meshGeometry);
      const mesh = new THREE.Mesh(meshGeometry, fillMaterial);
      const lines = new THREE.LineSegments(linesGeometry, stokeMaterial);
      svgGroup.add(mesh, lines);
    });
  });
});
svgGroup.position.set(pointInfo.x, pointInfo.y, z);
svgGroup.scale.set(0.001, 0.001, 0.001);
svgGroup.rotateX(80.1);
svgGroup.translateY(-0.5);
this.svgGroup = svgGroup;
this.scene.add(svgGroup);

} //涟漪 //片 ripple(pointInfo, z) { const geometry = new THREE.CircleGeometry(0.1, 32); const material = new THREE.MeshBasicMaterial({ color: 0xffff00 }); const circle = new THREE.Mesh(geometry, material); circle.translateY(-0.5); circle.position.set(pointInfo.x + 0.2, pointInfo.y, z + 0.001); this.circleAnimate = circle; this.scene.add(circle); this.huan(pointInfo, z); } //环 huan(pointInfo, z) { const geometry = new THREE.RingGeometry(0.15, 0.18, 100); const material = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide, }); const mesh = new THREE.Mesh(geometry, material); mesh.translateY(-0.5); mesh.position.set(pointInfo.x + 0.2, pointInfo.y, z + 0.001); mesh._s = 1; mesh._st = 0; this.huanmesh.push(mesh); this.meshAnimate = mesh; this.scene.add(mesh); } // 涟漪效果 cityWaveAnimate() { requestAnimationFrame(this.cityWaveAnimate.bind(this)); this.huanmesh.forEach((mesh) => { if (mesh._s >= 2) { mesh._st = 1; } if (mesh._s <= 1) { mesh._st = 0; }

  if (mesh._st) {
    mesh._s -= 0.009;
  } else {
    mesh._s += 0.009;
  }
  mesh.scale.set(mesh._s, mesh._s, mesh._s);
});
this.render();

} } export {chinaMap}