123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- import * as THREE from "three";
- import * as d3 from "d3";
- import { CSS2DObject } from "three/examples/jsm/renderers/CSS2DRenderer";
- import { Line2 } from "three/examples/jsm/lines/Line2";
- import { LineGeometry } from "three/examples/jsm/lines/LineGeometry";
- import { LineMaterial } from "three/examples/jsm/lines/LineMaterial";
- // import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
- // import { ProjectionFnParamType } from "./index.vue";
- import { mapConfig } from "./mapConfig";
- // 绘制挤出的材质
- export function drawExtrudeMesh(
- point,
- projectionFn
- ) {
- const shape = new THREE.Shape();
- const pointsArray = [];
- for (let i = 0; i < point.length; i++) {
- const [x, y] = projectionFn(point[i]); // 将每一个经纬度转化为坐标点
- if (i === 0) {
- shape.moveTo(x, -y);
- }
- shape.lineTo(x, -y);
- pointsArray.push(x, -y, mapConfig.topLineZIndex);
- }
- const geometry = new THREE.ExtrudeGeometry(shape, {
- depth: mapConfig.mapDepth, // 挤出的形状深度
- bevelEnabled: false, // 对挤出的形状应用是否斜角
- });
- const material = new THREE.MeshPhongMaterial({
- // color: mapConfig.mapColor,
- color: mapConfig.mapColorGradient[Math.floor(Math.random() * 4)], // 随机颜色
- // transparent: mapConfig.mapTransparent,
- // opacity: mapConfig.mapOpacity,
- });
- const materialSide = new THREE.ShaderMaterial({
- uniforms: {
- color1: {
- value: new THREE.Color(mapConfig.mapSideColor1),
- },
- color2: {
- value: new THREE.Color(mapConfig.mapSideColor2),
- },
- },
- vertexShader: `
- varying vec3 vPosition;
- void main() {
- vPosition = position;
- gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
- }
- `,
- fragmentShader: `
- uniform vec3 color1;
- uniform vec3 color2;
- varying vec3 vPosition;
- void main() {
- vec3 mixColor = mix(color1, color2, 0.5 - vPosition.z * 0.2); // 使用顶点坐标 z 分量来控制混合
- gl_FragColor = vec4(mixColor, 1.0);
- }
- `,
- // wireframe: true,
- });
- const mesh = new THREE.Mesh(geometry, [material, materialSide]);
- // userData 存储自定义属性
- mesh.userData = {
- isChangeColor: true,
- };
- // 边框线,赋值空间点坐标,3个一组
- const lineGeometry = new LineGeometry();
- lineGeometry.setPositions(pointsArray);
- const lineMaterial = new LineMaterial({
- color: mapConfig.topLineColor,
- linewidth: mapConfig.topLineWidth,
- });
- lineMaterial.resolution.set(window.innerWidth, window.innerHeight);
- const line = new Line2(lineGeometry, lineMaterial);
- return { mesh, line };
- }
- // 生成地图3D模型
- export function generateMapObject3D(
- mapdata,
- projectionFnParam
- ) {
- // 地图对象
- const mapObject3D = new THREE.Object3D();
- // 地图数据
- const { features: basicFeatures } = mapdata;
- const { center, scale } = projectionFnParam;
- const projectionFn = d3
- .geoMercator()
- .center(center)
- .scale(scale)
- .translate([0, 0]);
- const label2dData = []; // 存储自定义 2d 标签数据
- // 每个省的数据
- basicFeatures.forEach((basicFeatureItem) => {
- // 每个省份的地图对象
- const provinceMapObject3D = new THREE.Object3D();
- // 将地图数据挂在到模型数据上
- provinceMapObject3D.customProperties = basicFeatureItem.properties;
- // 每个坐标类型
- const featureType = basicFeatureItem.geometry.type;
- // 每个坐标数组
- const featureCoords =
- basicFeatureItem.geometry.coordinates;
- // 每个中心点位置
- const featureCenterCoord =
- basicFeatureItem.properties.centroid &&
- projectionFn(basicFeatureItem.properties.centroid);
- // 名字
- const featureName = basicFeatureItem.properties.name;
- if (featureCenterCoord) {
- label2dData.push({
- featureCenterCoord,
- featureName,
- });
- }
- // MultiPolygon 类型
- if (featureType === "MultiPolygon") {
- featureCoords.forEach((multiPolygon) => {
- multiPolygon.forEach((polygon) => {
- const { mesh, line } = drawExtrudeMesh(polygon, projectionFn);
- provinceMapObject3D.add(mesh);
- provinceMapObject3D.add(line);
- });
- });
- }
- // Polygon 类型
- if (featureType === "Polygon") {
- featureCoords.forEach((polygon) => {
- const { mesh, line } = drawExtrudeMesh(polygon, projectionFn);
- provinceMapObject3D.add(mesh);
- provinceMapObject3D.add(line);
- });
- }
- mapObject3D.add(provinceMapObject3D);
- });
- return { mapObject3D, label2dData };
- }
- // 生成地图2D标签
- export function generateMapLabel2D(label2dData) {
- const labelObject2D = new THREE.Object3D();
- label2dData.forEach((item) => {
- const { featureCenterCoord, featureName } = item;
- const labelObjectItem = draw2dLabel(featureCenterCoord, featureName);
- if (labelObjectItem) {
- labelObject2D.add(labelObjectItem);
- }
- });
- return labelObject2D;
- }
- // 生成地图spot点位
- export function generateMapSpot(label2dData) {
- const spotObject3D = new THREE.Object3D();
- const spotList = [];
- label2dData.forEach((item) => {
- const { featureCenterCoord } = item;
- const spotObjectItem = drawSpot(featureCenterCoord);
- if (spotObjectItem && spotObjectItem.circle && spotObjectItem.ring) {
- spotObject3D.add(spotObjectItem.circle);
- spotObject3D.add(spotObjectItem.ring);
- spotList.push(spotObjectItem.ring);
- }
- //TODO:渲染铁塔
-
- });
- return { spotObject3D, spotList };
- }
- // 绘制二维标签
- export const draw2dLabel = (coord, proviceName) => {
- if (coord && coord.length) {
- // 模版字符串
- const innerHTML = `<div class="your-classname" style="color: #fff">${proviceName}</div>`;
- const labelDiv = document.createElement("div");
- labelDiv.innerHTML = innerHTML;
- labelDiv.style.pointerEvents = "none"; // 禁用事件
- const labelObject = new CSS2DObject(labelDiv);
- labelObject.position.set(coord[0], -coord[1], mapConfig.label2dZIndex);
- return labelObject;
- }
- };
- // 绘制圆点
- export const drawSpot = (coord) => {
- if (coord && coord.length) {
- /**
- * 绘制圆点
- */
- const spotGeometry = new THREE.CircleGeometry(0.2, 200);
- const spotMaterial = new THREE.MeshBasicMaterial({
- color: "#3EC5FB",
- side: THREE.DoubleSide,
- });
- const circle = new THREE.Mesh(spotGeometry, spotMaterial);
- circle.position.set(coord[0], -coord[1], mapConfig.spotZIndex);
- // 圆环
- const ringGeometry = new THREE.RingGeometry(0.2, 0.3, 50);
- const ringMaterial = new THREE.MeshBasicMaterial({
- color: "#3FC5FB",
- side: THREE.DoubleSide,
- transparent: true,
- });
- const ring = new THREE.Mesh(ringGeometry, ringMaterial);
- ring.position.set(coord[0], -coord[1], mapConfig.spotZIndex);
- return { circle, ring };
- }
- };
- /**
- * 线上移动物体
- */
- export const drawflySpot = (curve) => {
- const aGeo = new THREE.SphereGeometry(0.2);
- const aMater = new THREE.MeshBasicMaterial({
- color: "#77f077",
- side: THREE.DoubleSide,
- });
- const aMesh = new THREE.Mesh(aGeo, aMater);
- // 保存曲线实例
- aMesh.curve = curve;
- aMesh._s = 0;
- return aMesh;
- };
- // 绘制两点链接飞线
- export const drawLineBetween2Spot = (
- coordStart,
- coordEnd
- ) => {
- const [x0, y0, z0] = [...coordStart, mapConfig.spotZIndex];
- const [x1, y1, z1] = [...coordEnd, mapConfig.spotZIndex];
- // 使用 QuadraticBezierCurve3 创建 三维二次贝塞尔曲线
- const curve = new THREE.QuadraticBezierCurve3(
- new THREE.Vector3(x0, -y0, z0),
- new THREE.Vector3((x0 + x1) / 2, -(y0 + y1) / 2, 20),
- new THREE.Vector3(x1, -y1, z1)
- );
- const flySpot = drawflySpot(curve);
- const lineGeometry = new THREE.BufferGeometry();
- // 获取曲线上50个点
- const points = curve.getPoints(50);
- const positions = [];
- const colors = [];
- const color = new THREE.Color();
- // 给每个顶点设置演示 实现渐变
- for (let j = 0; j < points.length; j++) {
- color.setHSL(0.21 + j, 0.77, 0.55 + j * 0.0025); // 色
- colors.push(color.r, color.g, color.b);
- positions.push(points[j].x, points[j].y, points[j].z);
- }
- // 放入顶点 和 设置顶点颜色
- lineGeometry.setAttribute(
- "position",
- new THREE.BufferAttribute(new Float32Array(positions), 3, true)
- );
- lineGeometry.setAttribute(
- "color",
- new THREE.BufferAttribute(new Float32Array(colors), 3, true)
- );
- const material = new THREE.LineBasicMaterial({
- vertexColors: true,
- // color: "red",
- side: THREE.DoubleSide,
- });
- const flyLine = new THREE.Line(lineGeometry, material);
- return { flyLine, flySpot };
- };
|