提交 e6670372 authored 作者: 詹银鑫's avatar 詹银鑫

加载无人机模型并添加点击弹窗和无人机动画

上级 d28ccc7e
...@@ -31,8 +31,8 @@ import { ...@@ -31,8 +31,8 @@ import {
FileLoader, FileLoader,
Shape, Shape,
ExtrudeGeometry, ExtrudeGeometry,
} from "three" } from "three";
import * as THREE from "three" import * as THREE from "three";
import { import {
Mini3d, Mini3d,
...@@ -46,39 +46,39 @@ import { ...@@ -46,39 +46,39 @@ import {
GradientShader, GradientShader,
DiffuseShader, DiffuseShader,
Focus, Focus,
} from "@/mini3d" } from "@/mini3d";
import { geoMercator } from "d3-geo" import { geoMercator } from "d3-geo";
import labelIcon from "@/assets/texture/label-icon.png" import labelIcon from "@/assets/texture/label-icon.png";
import chinaData from "./map/chinaData" import chinaData from "./map/chinaData";
import provincesData from "./map/provincesData" import provincesData from "./map/provincesData";
import scatterData from "./map/scatter" import scatterData from "./map/scatter";
import infoData from "./map/infoData" import infoData from "./map/infoData";
import gsap from "gsap" import gsap from "gsap";
import emitter from "@/utils/emitter" import emitter from "@/utils/emitter";
import { InteractionManager } from "three.interactive" import { InteractionManager } from "three.interactive";
// 引入模型加载模块 // 引入模型加载模块
import { Resource } from "@/mini3d/utils/Resource" import { Resource } from "@/mini3d/utils/Resource";
function sortByValue(data) { function sortByValue(data) {
data.sort((a, b) => b.value - a.value) data.sort((a, b) => b.value - a.value);
return data return data;
} }
export class World extends Mini3d { export class World extends Mini3d {
constructor(canvas, assets) { constructor(canvas, assets) {
super(canvas) super(canvas);
// 中心坐标 // 中心坐标
// this.geoProjectionCenter = [113.280637, 23.125178] // this.geoProjectionCenter = [113.280637, 23.125178]
this.geoProjectionCenter = [110.840171, 39.864362] this.geoProjectionCenter = [110.840171, 39.864362];
// 缩放比例 // 缩放比例
this.geoProjectionScale = 700 this.geoProjectionScale = 700;
// this.geoProjectionScale = 120 // this.geoProjectionScale = 120
// 飞线中心 // 飞线中心
// this.flyLineCenter = [113.544372, 23.329249] // this.flyLineCenter = [113.544372, 23.329249]
this.flyLineCenter = [111.319412,39.781603] this.flyLineCenter = [111.319412, 39.781603];
// 地图拉伸高度 // 地图拉伸高度
this.depth = 0.5 this.depth = 0.5;
// this.mapFocusLabelInfo = { // this.mapFocusLabelInfo = {
// name: "广东省", // name: "广东省",
// enName: "GUANGDONG PROVINCE", // enName: "GUANGDONG PROVINCE",
...@@ -88,76 +88,84 @@ export class World extends Mini3d { ...@@ -88,76 +88,84 @@ export class World extends Mini3d {
name: "准格尔旗", name: "准格尔旗",
enName: "ZHUNGEER QI", enName: "ZHUNGEER QI",
center: [110.840171, 39.864362], center: [110.840171, 39.864362],
} };
// 是否点击 // 是否点击
this.clicked = false this.clicked = false;
// 雾 // 雾
this.scene.fog = new Fog(0x102736, 1, 50) this.scene.fog = new Fog(0x102736, 1, 50);
// 背景 // 背景
this.scene.background = new Color(0x102736) this.scene.background = new Color(0x102736);
// 相机初始位置 // 相机初始位置
// this.camera.instance.position.set(-13.767695123014105, 12.990152163077308, 39.28228164159694) // this.camera.instance.position.set(-13.767695123014105, 12.990152163077308, 39.28228164159694)
this.camera.instance.position.set(-13.767695123014105, 12.990152163077308, 19.28228164159694) this.camera.instance.position.set(
this.camera.instance.near = 0.1 -13.767695123014105,
this.camera.instance.far = 10000 12.990152163077308,
this.camera.instance.updateProjectionMatrix() 19.28228164159694
);
this.camera.instance.near = 0.1;
this.camera.instance.far = 10000;
this.camera.instance.updateProjectionMatrix();
// 创建交互管理 // 创建交互管理
this.interactionManager = new InteractionManager(this.renderer.instance, this.camera.instance, this.canvas) this.interactionManager = new InteractionManager(
this.renderer.instance,
this.camera.instance,
this.canvas
);
this.assets = assets this.assets = assets;
// 创建环境光 // 创建环境光
this.initEnvironment() this.initEnvironment();
this.init() this.init();
console.log("执行了 这些代码") console.log("执行了 这些代码");
} }
init() { init() {
// 标签组 // 标签组
this.labelGroup = new Group() this.labelGroup = new Group();
this.label3d = new Label3d(this) this.label3d = new Label3d(this);
this.labelGroup.rotation.x = -Math.PI / 2 this.labelGroup.rotation.x = -Math.PI / 2;
this.scene.add(this.labelGroup) this.scene.add(this.labelGroup);
// 飞线焦点光圈组 // 飞线焦点光圈组
this.flyLineFocusGroup = new Group() this.flyLineFocusGroup = new Group();
this.flyLineFocusGroup.visible = false this.flyLineFocusGroup.visible = false;
this.flyLineFocusGroup.rotation.x = -Math.PI / 2 this.flyLineFocusGroup.rotation.x = -Math.PI / 2;
this.scene.add(this.flyLineFocusGroup) this.scene.add(this.flyLineFocusGroup);
// 区域事件元素 // 区域事件元素
this.eventElement = [] this.eventElement = [];
// 鼠标移上移除的材质 // 鼠标移上移除的材质
this.defaultMaterial = null // 默认材质 this.defaultMaterial = null; // 默认材质
this.defaultLightMaterial = null // 高亮材质 this.defaultLightMaterial = null; // 高亮材质
// 创建地形模型初始化 // 创建地形模型初始化
this.terrainResource = new Resource() this.terrainResource = new Resource();
// 创建底部高亮 // 创建底部高亮
this.createBottomBg() this.createBottomBg();
// 模糊边线 // 模糊边线
this.createChinaBlurLine() this.createChinaBlurLine();
// 扩散网格 // 扩散网格
this.createGrid() this.createGrid();
// 旋转圆环 // 旋转圆环
this.createRotateBorder() this.createRotateBorder();
// 创建标签 // 创建标签
this.createLabel(this.mapFocusLabelInfo) this.createLabel(this.mapFocusLabelInfo);
// 创建地图 // 创建地图
this.createMap() this.createMap();
// 添加事件 // 添加事件
this.createEvent() this.createEvent();
// 创建飞线 // 创建飞线
this.createFlyLine() this.createFlyLine();
// 创建飞线焦点 // 创建飞线焦点
this.createFocus() this.createFocus();
// 创建粒子 // 创建粒子
this.createParticles() this.createParticles();
// 创建散点图 // 创建散点图
this.createScatter() this.createScatter();
// 创建信息点 // 创建信息点
this.createInfoPoint() this.createInfoPoint();
// 创建轮廓 // 创建轮廓
this.createStorke() this.createStorke();
// 创建圆滑曲线 // 创建圆滑曲线
this.createSmoothLine([ this.createSmoothLine([
[110.9801, 39.8002], [110.9801, 39.8002],
...@@ -165,7 +173,14 @@ export class World extends Mini3d { ...@@ -165,7 +173,14 @@ export class World extends Mini3d {
[111.1803, 39.6054], [111.1803, 39.6054],
[110.9008, 39.8056], [110.9008, 39.8056],
[111.1003, 39.7386], [111.1003, 39.7386],
]) ]);
this.showDroneAtLngLat(
"../../../public/assets/json/Flying_drone..glb",
110.9801,
39.8002,
0.5,
5
);
// 创造3d地形 // 创造3d地形
// this.createTerrain() // this.createTerrain()
// 创造geoJson图 // 创造geoJson图
...@@ -176,12 +191,12 @@ export class World extends Mini3d { ...@@ -176,12 +191,12 @@ export class World extends Mini3d {
// 创建动画时间线 // 创建动画时间线
let tl = gsap.timeline({ let tl = gsap.timeline({
onComplete: () => {}, onComplete: () => {},
}) });
tl.pause() tl.pause();
this.animateTl = tl this.animateTl = tl;
tl.addLabel("focusMap", 1.5) tl.addLabel("focusMap", 1.5);
tl.addLabel("focusMapOpacity", 2) tl.addLabel("focusMapOpacity", 2);
tl.addLabel("bar", 3) tl.addLabel("bar", 3);
tl.to(this.camera.instance.position, { tl.to(this.camera.instance.position, {
duration: 2, duration: 2,
x: -0.17427287762525134, x: -0.17427287762525134,
...@@ -189,9 +204,9 @@ export class World extends Mini3d { ...@@ -189,9 +204,9 @@ export class World extends Mini3d {
z: 20.688611202093714, z: 20.688611202093714,
ease: "circ.out", ease: "circ.out",
onStart: () => { onStart: () => {
this.flyLineFocusGroup.visible = false this.flyLineFocusGroup.visible = false;
}, },
}) });
tl.to( tl.to(
this.focusMapGroup.position, this.focusMapGroup.position,
{ {
...@@ -201,7 +216,7 @@ export class World extends Mini3d { ...@@ -201,7 +216,7 @@ export class World extends Mini3d {
z: 0, z: 0,
}, },
"focusMap" "focusMap"
) );
tl.to( tl.to(
this.focusMapGroup.scale, this.focusMapGroup.scale,
...@@ -212,14 +227,14 @@ export class World extends Mini3d { ...@@ -212,14 +227,14 @@ export class World extends Mini3d {
z: 1, z: 1,
ease: "circ.out", ease: "circ.out",
onComplete: () => { onComplete: () => {
this.flyLineGroup.visible = true this.flyLineGroup.visible = true;
this.scatterGroup.visible = true this.scatterGroup.visible = true;
this.InfoPointGroup.visible = true this.InfoPointGroup.visible = true;
this.createInfoPointLabelLoop() this.createInfoPointLabelLoop();
}, },
}, },
"focusMap" "focusMap"
) );
tl.to( tl.to(
this.focusMapTopMaterial, this.focusMapTopMaterial,
...@@ -229,7 +244,7 @@ export class World extends Mini3d { ...@@ -229,7 +244,7 @@ export class World extends Mini3d {
ease: "circ.out", ease: "circ.out",
}, },
"focusMapOpacity" "focusMapOpacity"
) );
tl.to( tl.to(
this.focusMapSideMaterial, this.focusMapSideMaterial,
{ {
...@@ -237,13 +252,13 @@ export class World extends Mini3d { ...@@ -237,13 +252,13 @@ export class World extends Mini3d {
opacity: 1, opacity: 1,
ease: "circ.out", ease: "circ.out",
onComplete: () => { onComplete: () => {
this.focusMapSideMaterial.transparent = false this.focusMapSideMaterial.transparent = false;
}, },
}, },
"focusMapOpacity" "focusMapOpacity"
) );
this.otherLabel.map((item, index) => { this.otherLabel.map((item, index) => {
let element = item.element.querySelector(".other-label") let element = item.element.querySelector(".other-label");
tl.to( tl.to(
element, element,
{ {
...@@ -254,8 +269,8 @@ export class World extends Mini3d { ...@@ -254,8 +269,8 @@ export class World extends Mini3d {
ease: "circ.out", ease: "circ.out",
}, },
"focusMapOpacity" "focusMapOpacity"
) );
}) });
tl.to( tl.to(
this.mapLineMaterial, this.mapLineMaterial,
{ {
...@@ -264,7 +279,7 @@ export class World extends Mini3d { ...@@ -264,7 +279,7 @@ export class World extends Mini3d {
opacity: 1, opacity: 1,
}, },
"focusMapOpacity" "focusMapOpacity"
) );
tl.to( tl.to(
this.rotateBorder1.scale, this.rotateBorder1.scale,
{ {
...@@ -276,7 +291,7 @@ export class World extends Mini3d { ...@@ -276,7 +291,7 @@ export class World extends Mini3d {
ease: "circ.out", ease: "circ.out",
}, },
"focusMapOpacity" "focusMapOpacity"
) );
tl.to( tl.to(
this.rotateBorder2.scale, this.rotateBorder2.scale,
{ {
...@@ -287,15 +302,15 @@ export class World extends Mini3d { ...@@ -287,15 +302,15 @@ export class World extends Mini3d {
z: 1, z: 1,
ease: "circ.out", ease: "circ.out",
onComplete: () => { onComplete: () => {
this.flyLineFocusGroup.visible = true this.flyLineFocusGroup.visible = true;
emitter.$emit("mapPlayComplete") emitter.$emit("mapPlayComplete");
}, },
}, },
"focusMapOpacity" "focusMapOpacity"
) );
this.allBar.map((item, index) => { this.allBar.map((item, index) => {
if (item.userData.name === "广州市") { if (item.userData.name === "广州市") {
return false return false;
} }
tl.to( tl.to(
item.scale, item.scale,
...@@ -308,8 +323,8 @@ export class World extends Mini3d { ...@@ -308,8 +323,8 @@ export class World extends Mini3d {
ease: "circ.out", ease: "circ.out",
}, },
"bar" "bar"
) );
}) });
this.allBarMaterial.map((item, index) => { this.allBarMaterial.map((item, index) => {
tl.to( tl.to(
item, item,
...@@ -320,16 +335,16 @@ export class World extends Mini3d { ...@@ -320,16 +335,16 @@ export class World extends Mini3d {
ease: "circ.out", ease: "circ.out",
}, },
"bar" "bar"
) );
}) });
this.allProvinceLabel.map((item, index) => { this.allProvinceLabel.map((item, index) => {
let element = item.element.querySelector(".provinces-label-wrap") let element = item.element.querySelector(".provinces-label-wrap");
let number = item.element.querySelector(".number .value") let number = item.element.querySelector(".number .value");
let numberVal = Number(number.innerText) let numberVal = Number(number.innerText);
let numberAnimate = { let numberAnimate = {
score: 0, score: 0,
} };
tl.to( tl.to(
element, element,
{ {
...@@ -340,7 +355,7 @@ export class World extends Mini3d { ...@@ -340,7 +355,7 @@ export class World extends Mini3d {
ease: "circ.out", ease: "circ.out",
}, },
"bar" "bar"
) );
tl.to( tl.to(
numberAnimate, numberAnimate,
{ {
...@@ -350,11 +365,11 @@ export class World extends Mini3d { ...@@ -350,11 +365,11 @@ export class World extends Mini3d {
onUpdate: showScore, onUpdate: showScore,
}, },
"bar" "bar"
) );
function showScore() { function showScore() {
number.innerText = numberAnimate.score.toFixed(0) number.innerText = numberAnimate.score.toFixed(0);
} }
}) });
this.allGuangquan.map((item, index) => { this.allGuangquan.map((item, index) => {
tl.to( tl.to(
item.children[0].scale, item.children[0].scale,
...@@ -367,7 +382,7 @@ export class World extends Mini3d { ...@@ -367,7 +382,7 @@ export class World extends Mini3d {
ease: "circ.out", ease: "circ.out",
}, },
"bar" "bar"
) );
tl.to( tl.to(
item.children[1].scale, item.children[1].scale,
{ {
...@@ -379,20 +394,20 @@ export class World extends Mini3d { ...@@ -379,20 +394,20 @@ export class World extends Mini3d {
ease: "circ.out", ease: "circ.out",
}, },
"bar" "bar"
) );
}) });
} }
initEnvironment() { initEnvironment() {
let sun = new AmbientLight(0xffffff, 5) let sun = new AmbientLight(0xffffff, 5);
this.scene.add(sun) this.scene.add(sun);
let directionalLight = new DirectionalLight(0xffffff, 5) let directionalLight = new DirectionalLight(0xffffff, 5);
directionalLight.position.set(-30, 6, -8) directionalLight.position.set(-30, 6, -8);
directionalLight.castShadow = true directionalLight.castShadow = true;
directionalLight.shadow.radius = 20 directionalLight.shadow.radius = 20;
directionalLight.shadow.mapSize.width = 1024 directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024 directionalLight.shadow.mapSize.height = 1024;
this.scene.add(directionalLight) this.scene.add(directionalLight);
this.createPointLight({ this.createPointLight({
color: "#1d5e5e", color: "#1d5e5e",
intensity: 800, intensity: 800,
...@@ -400,7 +415,7 @@ export class World extends Mini3d { ...@@ -400,7 +415,7 @@ export class World extends Mini3d {
x: -9, x: -9,
y: 3, y: 3,
z: -3, z: -3,
}) });
this.createPointLight({ this.createPointLight({
color: "#1d5e5e", color: "#1d5e5e",
intensity: 200, intensity: 200,
...@@ -409,50 +424,54 @@ export class World extends Mini3d { ...@@ -409,50 +424,54 @@ export class World extends Mini3d {
// y: 2, // y: 2,
y: -2, y: -2,
z: 5, z: 5,
}) });
} }
createPointLight(pointParams) { createPointLight(pointParams) {
const pointLight = new PointLight(0x1d5e5e, pointParams.intensity, pointParams.distance) const pointLight = new PointLight(
pointLight.position.set(pointParams.x, pointParams.y, pointParams.z) 0x1d5e5e,
this.scene.add(pointLight) pointParams.intensity,
pointParams.distance
);
pointLight.position.set(pointParams.x, pointParams.y, pointParams.z);
this.scene.add(pointLight);
} }
createMap() { createMap() {
let mapGroup = new Group() let mapGroup = new Group();
let focusMapGroup = new Group() let focusMapGroup = new Group();
this.focusMapGroup = focusMapGroup this.focusMapGroup = focusMapGroup;
let { china, chinaTopLine } = this.createChina() let { china, chinaTopLine } = this.createChina();
let { map, mapTop, mapLine } = this.createProvince() let { map, mapTop, mapLine } = this.createProvince();
let { waterGroup } = this.createWater() let { waterGroup } = this.createWater();
let { lineWaterGroup } = this.createLineWater() let { lineWaterGroup } = this.createLineWater();
china.setParent(mapGroup) china.setParent(mapGroup);
chinaTopLine.setParent(mapGroup) chinaTopLine.setParent(mapGroup);
// 创建扩散 // 创建扩散
this.createDiffuse() this.createDiffuse();
map.setParent(focusMapGroup) map.setParent(focusMapGroup);
mapTop.setParent(focusMapGroup) mapTop.setParent(focusMapGroup);
mapLine.setParent(focusMapGroup) mapLine.setParent(focusMapGroup);
focusMapGroup.add(waterGroup) focusMapGroup.add(waterGroup);
focusMapGroup.add(lineWaterGroup) focusMapGroup.add(lineWaterGroup);
focusMapGroup.position.set(0, 0, -0.01) focusMapGroup.position.set(0, 0, -0.01);
focusMapGroup.scale.set(1, 1, 0) focusMapGroup.scale.set(1, 1, 0);
focusMapGroup.name = "focusMapGroup" focusMapGroup.name = "focusMapGroup";
mapGroup.add(focusMapGroup) mapGroup.add(focusMapGroup);
mapGroup.rotation.x = -Math.PI / 2 mapGroup.rotation.x = -Math.PI / 2;
mapGroup.position.set(0, 0.2, 0) mapGroup.position.set(0, 0.2, 0);
this.scene.add(mapGroup) this.scene.add(mapGroup);
this.createBar() this.createBar();
} }
createChina() { createChina() {
let params = { let params = {
chinaBgMaterialColor: "#152c47", chinaBgMaterialColor: "#152c47",
lineColor: "#3f82cd", lineColor: "#3f82cd",
} };
let chinaData = this.assets.instance.getResource("china") let chinaData = this.assets.instance.getResource("china");
let chinaBgMaterial = new MeshLambertMaterial({ let chinaBgMaterial = new MeshLambertMaterial({
color: new Color(params.chinaBgMaterialColor), color: new Color(params.chinaBgMaterialColor),
transparent: true, transparent: true,
opacity: 1, opacity: 1,
}) });
let china = new BaseMap(this, { let china = new BaseMap(this, {
//position: new Vector3(0, 0, -0.03), //position: new Vector3(0, 0, -0.03),
data: chinaData, data: chinaData,
...@@ -461,10 +480,10 @@ export class World extends Mini3d { ...@@ -461,10 +480,10 @@ export class World extends Mini3d {
merge: true, merge: true,
material: chinaBgMaterial, material: chinaBgMaterial,
renderOrder: 2, renderOrder: 2,
}) });
let chinaTopLineMaterial = new LineBasicMaterial({ let chinaTopLineMaterial = new LineBasicMaterial({
color: params.lineColor, color: params.lineColor,
}) });
let chinaTopLine = new Line(this, { let chinaTopLine = new Line(this, {
// position: new Vector3(0, 0, -0.02), // position: new Vector3(0, 0, -0.02),
data: chinaData, data: chinaData,
...@@ -472,15 +491,15 @@ export class World extends Mini3d { ...@@ -472,15 +491,15 @@ export class World extends Mini3d {
geoProjectionScale: this.geoProjectionScale, geoProjectionScale: this.geoProjectionScale,
material: chinaTopLineMaterial, material: chinaTopLineMaterial,
renderOrder: 3, renderOrder: 3,
}) });
chinaTopLine.lineGroup.position.z += 0.01 chinaTopLine.lineGroup.position.z += 0.01;
return { china, chinaTopLine } return { china, chinaTopLine };
} }
createProvince() { createProvince() {
let mapJsonData = this.assets.instance.getResource("mapJson") let mapJsonData = this.assets.instance.getResource("mapJson");
let [topMaterial, sideMaterial] = this.createProvinceMaterial() let [topMaterial, sideMaterial] = this.createProvinceMaterial();
this.focusMapTopMaterial = topMaterial this.focusMapTopMaterial = topMaterial;
this.focusMapSideMaterial = sideMaterial this.focusMapSideMaterial = sideMaterial;
let map = new ExtrudeMap(this, { let map = new ExtrudeMap(this, {
geoProjectionCenter: this.geoProjectionCenter, geoProjectionCenter: this.geoProjectionCenter,
geoProjectionScale: this.geoProjectionScale, geoProjectionScale: this.geoProjectionScale,
...@@ -490,23 +509,23 @@ export class World extends Mini3d { ...@@ -490,23 +509,23 @@ export class World extends Mini3d {
topFaceMaterial: topMaterial, topFaceMaterial: topMaterial,
sideMaterial: sideMaterial, sideMaterial: sideMaterial,
renderOrder: 9, renderOrder: 9,
}) });
let faceMaterial = new MeshStandardMaterial({ let faceMaterial = new MeshStandardMaterial({
color: 0xffffff, color: 0xffffff,
transparent: true, transparent: true,
opacity: 0.5, opacity: 0.5,
// fog: false, // fog: false,
}) });
let faceGradientShader = new GradientShader(faceMaterial, { let faceGradientShader = new GradientShader(faceMaterial, {
// uColor1: 0x2a6e92, // uColor1: 0x2a6e92,
// uColor2: 0x102736, // uColor2: 0x102736,
uColor1: 0x12bbe0, uColor1: 0x12bbe0,
uColor2: 0x0094b5, uColor2: 0x0094b5,
}) });
this.defaultMaterial = faceMaterial this.defaultMaterial = faceMaterial;
this.defaultLightMaterial = this.defaultMaterial.clone() this.defaultLightMaterial = this.defaultMaterial.clone();
this.defaultLightMaterial.color = new Color("rgba(115,208,255,1)") this.defaultLightMaterial.color = new Color("rgba(115,208,255,1)");
this.defaultLightMaterial.opacity = 0.8 this.defaultLightMaterial.opacity = 0.8;
// this.defaultLightMaterial.emissive.setHex(new Color("rgba(115,208,255,1)")); // this.defaultLightMaterial.emissive.setHex(new Color("rgba(115,208,255,1)"));
// this.defaultLightMaterial.emissiveIntensity = 3.5; // this.defaultLightMaterial.emissiveIntensity = 3.5;
let mapTop = new BaseMap(this, { let mapTop = new BaseMap(this, {
...@@ -516,34 +535,34 @@ export class World extends Mini3d { ...@@ -516,34 +535,34 @@ export class World extends Mini3d {
data: mapJsonData, data: mapJsonData,
material: faceMaterial, material: faceMaterial,
renderOrder: 2, renderOrder: 2,
}) });
mapTop.mapGroup.children.map((group) => { mapTop.mapGroup.children.map((group) => {
group.name = group.userData.name group.name = group.userData.name;
group.children.map((mesh) => { group.children.map((mesh) => {
if (mesh.type === "Mesh") { if (mesh.type === "Mesh") {
this.eventElement.push(mesh) this.eventElement.push(mesh);
} }
}) });
}) });
this.mapLineMaterial = new LineBasicMaterial({ this.mapLineMaterial = new LineBasicMaterial({
color: 0xffffff, color: 0xffffff,
opacity: 0, opacity: 0,
transparent: true, transparent: true,
fog: false, fog: false,
}) });
let mapLine = new Line(this, { let mapLine = new Line(this, {
geoProjectionCenter: this.geoProjectionCenter, geoProjectionCenter: this.geoProjectionCenter,
geoProjectionScale: this.geoProjectionScale, geoProjectionScale: this.geoProjectionScale,
data: mapJsonData, data: mapJsonData,
material: this.mapLineMaterial, material: this.mapLineMaterial,
renderOrder: 3, renderOrder: 3,
}) });
mapLine.lineGroup.position.z += this.depth + 0.23 mapLine.lineGroup.position.z += this.depth + 0.23;
return { return {
map, map,
mapTop, mapTop,
mapLine, mapLine,
} };
} }
createProvinceMaterial() { createProvinceMaterial() {
let topMaterial = new MeshLambertMaterial({ let topMaterial = new MeshLambertMaterial({
...@@ -552,13 +571,13 @@ export class World extends Mini3d { ...@@ -552,13 +571,13 @@ export class World extends Mini3d {
opacity: 0, opacity: 0,
fog: false, fog: false,
side: DoubleSide, side: DoubleSide,
}) });
topMaterial.onBeforeCompile = (shader) => { topMaterial.onBeforeCompile = (shader) => {
shader.uniforms = { shader.uniforms = {
...shader.uniforms, ...shader.uniforms,
uColor1: { value: new Color(0x2a6e92) }, // 419daa uColor1: { value: new Color(0x2a6e92) }, // 419daa
uColor2: { value: new Color(0x102736) }, uColor2: { value: new Color(0x102736) },
} };
shader.vertexShader = shader.vertexShader.replace( shader.vertexShader = shader.vertexShader.replace(
"void main() {", "void main() {",
` `
...@@ -569,7 +588,7 @@ export class World extends Mini3d { ...@@ -569,7 +588,7 @@ export class World extends Mini3d {
vAlpha = alpha; vAlpha = alpha;
vPosition = position; vPosition = position;
` `
) );
shader.fragmentShader = shader.fragmentShader.replace( shader.fragmentShader = shader.fragmentShader.replace(
"void main() {", "void main() {",
` `
...@@ -579,7 +598,7 @@ export class World extends Mini3d { ...@@ -579,7 +598,7 @@ export class World extends Mini3d {
uniform vec3 uColor2; uniform vec3 uColor2;
void main() { void main() {
` `
) );
shader.fragmentShader = shader.fragmentShader.replace( shader.fragmentShader = shader.fragmentShader.replace(
"#include <opaque_fragment>", "#include <opaque_fragment>",
/* glsl */ ` /* glsl */ `
...@@ -597,29 +616,29 @@ export class World extends Mini3d { ...@@ -597,29 +616,29 @@ export class World extends Mini3d {
} }
gl_FragColor = vec4( outgoingLight, diffuseColor.a ); gl_FragColor = vec4( outgoingLight, diffuseColor.a );
` `
) );
} };
let sideMap = this.assets.instance.getResource("side") let sideMap = this.assets.instance.getResource("side");
sideMap.wrapS = RepeatWrapping sideMap.wrapS = RepeatWrapping;
sideMap.wrapT = RepeatWrapping sideMap.wrapT = RepeatWrapping;
sideMap.repeat.set(1, 1.5) sideMap.repeat.set(1, 1.5);
sideMap.offset.y += 0.065 sideMap.offset.y += 0.065;
let sideMaterial = new MeshStandardMaterial({ let sideMaterial = new MeshStandardMaterial({
color: 0xffffff, color: 0xffffff,
map: sideMap, map: sideMap,
fog: false, fog: false,
opacity: 0, opacity: 0,
side: DoubleSide, side: DoubleSide,
}) });
this.time.on("tick", () => { this.time.on("tick", () => {
sideMap.offset.y += 0.005 sideMap.offset.y += 0.005;
}) });
sideMaterial.onBeforeCompile = (shader) => { sideMaterial.onBeforeCompile = (shader) => {
shader.uniforms = { shader.uniforms = {
...shader.uniforms, ...shader.uniforms,
uColor1: { value: new Color(0x2a6e92) }, uColor1: { value: new Color(0x2a6e92) },
uColor2: { value: new Color(0x2a6e92) }, uColor2: { value: new Color(0x2a6e92) },
} };
shader.vertexShader = shader.vertexShader.replace( shader.vertexShader = shader.vertexShader.replace(
"void main() {", "void main() {",
` `
...@@ -630,7 +649,7 @@ export class World extends Mini3d { ...@@ -630,7 +649,7 @@ export class World extends Mini3d {
vAlpha = alpha; vAlpha = alpha;
vPosition = position; vPosition = position;
` `
) );
shader.fragmentShader = shader.fragmentShader.replace( shader.fragmentShader = shader.fragmentShader.replace(
"void main() {", "void main() {",
` `
...@@ -640,7 +659,7 @@ export class World extends Mini3d { ...@@ -640,7 +659,7 @@ export class World extends Mini3d {
uniform vec3 uColor2; uniform vec3 uColor2;
void main() { void main() {
` `
) );
shader.fragmentShader = shader.fragmentShader.replace( shader.fragmentShader = shader.fragmentShader.replace(
"#include <opaque_fragment>", "#include <opaque_fragment>",
/* glsl */ ` /* glsl */ `
...@@ -654,64 +673,73 @@ export class World extends Mini3d { ...@@ -654,64 +673,73 @@ export class World extends Mini3d {
outgoingLight = outgoingLight*gradient; outgoingLight = outgoingLight*gradient;
gl_FragColor = vec4( outgoingLight, diffuseColor.a ); gl_FragColor = vec4( outgoingLight, diffuseColor.a );
` `
) );
} };
return [topMaterial, sideMaterial] return [topMaterial, sideMaterial];
} }
createBar() { createBar() {
let self = this let self = this;
let data = sortByValue(provincesData).filter((item, index) => index < 7) let data = sortByValue(provincesData).filter((item, index) => index < 7);
const barGroup = new Group() const barGroup = new Group();
this.barGroup = barGroup this.barGroup = barGroup;
const factor = 0.7 const factor = 0.7;
const height = 4.0 * factor const height = 4.0 * factor;
const max = data[0].value const max = data[0].value;
this.allBar = [] this.allBar = [];
this.allBarMaterial = [] this.allBarMaterial = [];
this.allGuangquan = [] this.allGuangquan = [];
this.allProvinceLabel = [] this.allProvinceLabel = [];
data.map((item, index) => { data.map((item, index) => {
let geoHeight = height * (item.value / max) let geoHeight = height * (item.value / max);
let material = new MeshBasicMaterial({ let material = new MeshBasicMaterial({
color: 0xffffff, color: 0xffffff,
transparent: true, transparent: true,
opacity: 0, opacity: 0,
depthTest: false, depthTest: false,
fog: false, fog: false,
}) });
new GradientShader(material, { new GradientShader(material, {
uColor1: index > 3 ? 0xfbdf88 : 0x50bbfe, uColor1: index > 3 ? 0xfbdf88 : 0x50bbfe,
uColor2: index > 3 ? 0xfffef4 : 0x77fbf5, uColor2: index > 3 ? 0xfffef4 : 0x77fbf5,
size: geoHeight, size: geoHeight,
dir: "y", dir: "y",
}) });
const geo = new BoxGeometry(0.1 * factor, 0.1 * factor, geoHeight) const geo = new BoxGeometry(0.1 * factor, 0.1 * factor, geoHeight);
geo.translate(0, 0, geoHeight / 2) geo.translate(0, 0, geoHeight / 2);
const mesh = new Mesh(geo, material) const mesh = new Mesh(geo, material);
mesh.renderOrder = 5 mesh.renderOrder = 5;
let areaBar = mesh let areaBar = mesh;
let [x, y] = this.geoProjection(item.centroid) let [x, y] = this.geoProjection(item.centroid);
areaBar.position.set(x, -y, this.depth + 0.45) areaBar.position.set(x, -y, this.depth + 0.45);
areaBar.scale.set(1, 1, 0) areaBar.scale.set(1, 1, 0);
areaBar.userData = { ...item } areaBar.userData = { ...item };
let guangQuan = this.createQuan(new Vector3(x, this.depth + 0.44, y), index) let guangQuan = this.createQuan(
let hg = this.createHUIGUANG(geoHeight, index > 3 ? 0xfffef4 : 0x77fbf5) new Vector3(x, this.depth + 0.44, y),
areaBar.add(...hg) index
barGroup.add(areaBar) );
barGroup.rotation.x = -Math.PI / 2 let hg = this.createHUIGUANG(geoHeight, index > 3 ? 0xfffef4 : 0x77fbf5);
let barLabel = labelStyle04(item, index, new Vector3(x, -y, this.depth + 1.1 + geoHeight)) areaBar.add(...hg);
this.allBar.push(areaBar) barGroup.add(areaBar);
this.allBarMaterial.push(material) barGroup.rotation.x = -Math.PI / 2;
this.allGuangquan.push(guangQuan) let barLabel = labelStyle04(
this.allProvinceLabel.push(barLabel) item,
}) index,
this.scene.add(barGroup) new Vector3(x, -y, this.depth + 1.1 + geoHeight)
);
this.allBar.push(areaBar);
this.allBarMaterial.push(material);
this.allGuangquan.push(guangQuan);
this.allProvinceLabel.push(barLabel);
});
this.scene.add(barGroup);
function labelStyle04(data, index, position) { function labelStyle04(data, index, position) {
let label = self.label3d.create("", "provinces-label", false) let label = self.label3d.create("", "provinces-label", false);
label.init( label.init(
`<div class="provinces-label ${index > 4 ? "yellow" : ""}"> `<div class="provinces-label ${index > 4 ? "yellow" : ""}">
<div class="provinces-label-wrap"> <div class="provinces-label-wrap">
<div class="number"><span class="value">${data.value}</span><span class="unit">万人</span></div> <div class="number"><span class="value">${
data.value
}</span><span class="unit">万人</span></div>
<div class="name"> <div class="name">
<span class="zh">${data.name}</span> <span class="zh">${data.name}</span>
<span class="en">${data.enName.toUpperCase()}</span> <span class="en">${data.enName.toUpperCase()}</span>
...@@ -720,84 +748,86 @@ export class World extends Mini3d { ...@@ -720,84 +748,86 @@ export class World extends Mini3d {
</div> </div>
</div>`, </div>`,
position position
) );
self.label3d.setLabelStyle(label, 0.01, "x") self.label3d.setLabelStyle(label, 0.01, "x");
label.setParent(self.labelGroup) label.setParent(self.labelGroup);
return label return label;
} }
} }
createEvent() { createEvent() {
let objectsHover = [] let objectsHover = [];
const reset = (mesh) => { const reset = (mesh) => {
mesh.traverse((obj) => { mesh.traverse((obj) => {
if (obj.isMesh) { if (obj.isMesh) {
obj.material = this.defaultMaterial obj.material = this.defaultMaterial;
}
})
} }
});
};
const move = (mesh) => { const move = (mesh) => {
mesh.traverse((obj) => { mesh.traverse((obj) => {
if (obj.isMesh) { if (obj.isMesh) {
obj.material = this.defaultLightMaterial obj.material = this.defaultLightMaterial;
}
})
} }
});
};
this.eventElement.map((mesh) => { this.eventElement.map((mesh) => {
this.interactionManager.add(mesh) this.interactionManager.add(mesh);
mesh.addEventListener("mousedown", (ev) => { mesh.addEventListener("mousedown", (ev) => {
let countryName = ev.target.userData.name let countryName = ev.target.userData.name;
this.countryData = ev.target.parent.parent.children this.countryData = ev.target.parent.parent.children;
if(ev.originalEvent.buttons === 1) { if (ev.originalEvent.buttons === 1) {
console.log("label",this.label112) console.log("label", this.label112);
this.focusMapGroup.children[0].visible = false this.focusMapGroup.children[0].visible = false;
this.countryData.map((item) => { this.countryData.map((item) => {
// console.log("this.mapFocusLabel",this.mapFocusLabel) // console.log("this.mapFocusLabel",this.mapFocusLabel)
if (item.name != countryName) { if (item.name != countryName) {
item.visible = false // 隐藏其他区域 item.visible = false; // 隐藏其他区域
} else { } else {
console.log("item",item) console.log("item", item);
item.visible = true // 显示被点击的区域 item.visible = true; // 显示被点击的区域
const [x, y] = this.geoProjection([109.840171, 38.864362]) const [x, y] = this.geoProjection([109.840171, 38.864362]);
// console.log("mapFocusLabel",this.mapFocusLabel) // console.log("mapFocusLabel",this.mapFocusLabel)
this.mapFocusLabel.element.innerHTML = `<div class="other-label" style="translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px, 0px);"><span>${item.name}</span><span>ZHUNGEERQI</span></div>` this.mapFocusLabel.element.innerHTML = `<div class="other-label" style="translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px, 0px);"><span>${item.name}</span><span>ZHUNGEERQI</span></div>`;
// this.mapFocusLabel.position.set(x, -y, 0.4); // this.mapFocusLabel.position.set(x, -y, 0.4);
} }
}) });
} }
}) });
mesh.addEventListener("mouseover", (event) => { mesh.addEventListener("mouseover", (event) => {
if (!objectsHover.includes(event.target.parent)) { if (!objectsHover.includes(event.target.parent)) {
objectsHover.push(event.target.parent) objectsHover.push(event.target.parent);
} }
document.body.style.cursor = "pointer" document.body.style.cursor = "pointer";
move(event.target.parent) move(event.target.parent);
}) });
mesh.addEventListener("mouseout", (event) => { mesh.addEventListener("mouseout", (event) => {
objectsHover = objectsHover.filter((n) => n.userData.name !== event.target.parent.userData.name) objectsHover = objectsHover.filter(
(n) => n.userData.name !== event.target.parent.userData.name
);
if (objectsHover.length > 0) { if (objectsHover.length > 0) {
const mesh = objectsHover[objectsHover.length - 1] const mesh = objectsHover[objectsHover.length - 1];
} }
reset(event.target.parent) reset(event.target.parent);
document.body.style.cursor = "default" document.body.style.cursor = "default";
}) });
}) });
document.addEventListener("contextmenu",(event) => { document.addEventListener("contextmenu", (event) => {
this.focusMapGroup.children[0].visible = true this.focusMapGroup.children[0].visible = true;
if(this.countryData.length > 0) { if (this.countryData.length > 0) {
this.countryData.map((item) => { this.countryData.map((item) => {
item.visible = true // 显示所有区域 item.visible = true; // 显示所有区域
this.mapFocusLabel.element.innerHTML = `<div class="other-label" style="translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px, 0px);"><span>准格尔旗</span><span>ZHUNGEER QI</span></div>` this.mapFocusLabel.element.innerHTML = `<div class="other-label" style="translate: none; rotate: none; scale: none; opacity: 1; transform: translate(0px, 0px);"><span>准格尔旗</span><span>ZHUNGEER QI</span></div>`;
}) });
} }
}) });
} }
createHUIGUANG(h, color) { createHUIGUANG(h, color) {
let geometry = new PlaneGeometry(0.35, h) let geometry = new PlaneGeometry(0.35, h);
geometry.translate(0, h / 2, 0) geometry.translate(0, h / 2, 0);
const texture = this.assets.instance.getResource("huiguang") const texture = this.assets.instance.getResource("huiguang");
texture.colorSpace = SRGBColorSpace texture.colorSpace = SRGBColorSpace;
texture.wrapS = RepeatWrapping texture.wrapS = RepeatWrapping;
texture.wrapT = RepeatWrapping texture.wrapT = RepeatWrapping;
let material = new MeshBasicMaterial({ let material = new MeshBasicMaterial({
color: color, color: color,
map: texture, map: texture,
...@@ -806,20 +836,20 @@ export class World extends Mini3d { ...@@ -806,20 +836,20 @@ export class World extends Mini3d {
depthWrite: false, depthWrite: false,
side: DoubleSide, side: DoubleSide,
blending: AdditiveBlending, blending: AdditiveBlending,
}) });
let mesh = new Mesh(geometry, material) let mesh = new Mesh(geometry, material);
mesh.renderOrder = 10 mesh.renderOrder = 10;
mesh.rotateX(Math.PI / 2) mesh.rotateX(Math.PI / 2);
let mesh2 = mesh.clone() let mesh2 = mesh.clone();
let mesh3 = mesh.clone() let mesh3 = mesh.clone();
mesh2.rotateY((Math.PI / 180) * 60) mesh2.rotateY((Math.PI / 180) * 60);
mesh3.rotateY((Math.PI / 180) * 120) mesh3.rotateY((Math.PI / 180) * 120);
return [mesh, mesh2, mesh3] return [mesh, mesh2, mesh3];
} }
createQuan(position, index) { createQuan(position, index) {
const guangquan1 = this.assets.instance.getResource("guangquan1") const guangquan1 = this.assets.instance.getResource("guangquan1");
const guangquan2 = this.assets.instance.getResource("guangquan2") const guangquan2 = this.assets.instance.getResource("guangquan2");
let geometry = new PlaneGeometry(0.5, 0.5) let geometry = new PlaneGeometry(0.5, 0.5);
let material1 = new MeshBasicMaterial({ let material1 = new MeshBasicMaterial({
color: 0xffffff, color: 0xffffff,
map: guangquan1, map: guangquan1,
...@@ -829,7 +859,7 @@ export class World extends Mini3d { ...@@ -829,7 +859,7 @@ export class World extends Mini3d {
depthTest: false, depthTest: false,
fog: false, fog: false,
blending: AdditiveBlending, blending: AdditiveBlending,
}) });
let material2 = new MeshBasicMaterial({ let material2 = new MeshBasicMaterial({
color: 0xffffff, color: 0xffffff,
map: guangquan2, map: guangquan2,
...@@ -839,40 +869,40 @@ export class World extends Mini3d { ...@@ -839,40 +869,40 @@ export class World extends Mini3d {
depthTest: false, depthTest: false,
fog: false, fog: false,
blending: AdditiveBlending, blending: AdditiveBlending,
}) });
let mesh1 = new Mesh(geometry, material1) let mesh1 = new Mesh(geometry, material1);
let mesh2 = new Mesh(geometry, material2) let mesh2 = new Mesh(geometry, material2);
mesh1.renderOrder = 6 mesh1.renderOrder = 6;
mesh2.renderOrder = 6 mesh2.renderOrder = 6;
mesh1.rotateX(-Math.PI / 2) mesh1.rotateX(-Math.PI / 2);
mesh2.rotateX(-Math.PI / 2) mesh2.rotateX(-Math.PI / 2);
mesh1.position.copy(position) mesh1.position.copy(position);
mesh2.position.copy(position) mesh2.position.copy(position);
mesh2.position.y -= 0.001 mesh2.position.y -= 0.001;
mesh1.scale.set(0, 0, 0) mesh1.scale.set(0, 0, 0);
mesh2.scale.set(0, 0, 0) mesh2.scale.set(0, 0, 0);
this.quanGroup = new Group() this.quanGroup = new Group();
this.quanGroup.add(mesh1, mesh2) this.quanGroup.add(mesh1, mesh2);
this.scene.add(this.quanGroup) this.scene.add(this.quanGroup);
this.time.on("tick", () => { this.time.on("tick", () => {
mesh1.rotation.z += 0.05 mesh1.rotation.z += 0.05;
}) });
return this.quanGroup return this.quanGroup;
} }
// 创建扩散 // 创建扩散
createDiffuse() { createDiffuse() {
let geometry = new PlaneGeometry(200, 200) let geometry = new PlaneGeometry(200, 200);
let material = new MeshBasicMaterial({ let material = new MeshBasicMaterial({
color: 0x000000, color: 0x000000,
depthWrite: false, depthWrite: false,
// depthTest: false, // depthTest: false,
transparent: true, transparent: true,
blending: CustomBlending, blending: CustomBlending,
}) });
// 使用CustomBlending 实现混合叠加 // 使用CustomBlending 实现混合叠加
material.blendEquation = AddEquation material.blendEquation = AddEquation;
material.blendSrc = DstColorFactor material.blendSrc = DstColorFactor;
material.blendDst = OneFactor material.blendDst = OneFactor;
let diffuse = new DiffuseShader({ let diffuse = new DiffuseShader({
material, material,
time: this.time, time: this.time,
...@@ -887,15 +917,15 @@ export class World extends Mini3d { ...@@ -887,15 +917,15 @@ export class World extends Mini3d {
repeat: -1, repeat: -1,
duration: 6, duration: 6,
ease: "power1.easeIn", ease: "power1.easeIn",
}) });
}, 3) }, 3);
}, },
}) });
let mesh = new Mesh(geometry, material) let mesh = new Mesh(geometry, material);
mesh.renderOrder = 3 mesh.renderOrder = 3;
mesh.rotation.x = -Math.PI / 2 mesh.rotation.x = -Math.PI / 2;
mesh.position.set(0, 0.21, 0) mesh.position.set(0, 0.21, 0);
this.scene.add(mesh) this.scene.add(mesh);
} }
createGrid() { createGrid() {
new Grid(this, { new Grid(this, {
...@@ -906,63 +936,63 @@ export class World extends Mini3d { ...@@ -906,63 +936,63 @@ export class World extends Mini3d {
shapeColor: 0x2a5f8a, shapeColor: 0x2a5f8a,
pointSize: 0.1, pointSize: 0.1,
pointColor: 0x154d7d, pointColor: 0x154d7d,
}) });
} }
createBottomBg() { createBottomBg() {
let geometry = new PlaneGeometry(20, 20) let geometry = new PlaneGeometry(20, 20);
const texture = this.assets.instance.getResource("ocean") const texture = this.assets.instance.getResource("ocean");
texture.colorSpace = SRGBColorSpace texture.colorSpace = SRGBColorSpace;
texture.wrapS = RepeatWrapping texture.wrapS = RepeatWrapping;
texture.wrapT = RepeatWrapping texture.wrapT = RepeatWrapping;
texture.repeat.set(1, 1) texture.repeat.set(1, 1);
let material = new MeshBasicMaterial({ let material = new MeshBasicMaterial({
map: texture, map: texture,
opacity: 1, opacity: 1,
fog: false, fog: false,
}) });
let mesh = new Mesh(geometry, material) let mesh = new Mesh(geometry, material);
mesh.rotation.x = -Math.PI / 2 mesh.rotation.x = -Math.PI / 2;
mesh.position.set(0, -0.7, 0) mesh.position.set(0, -0.7, 0);
this.scene.add(mesh) this.scene.add(mesh);
} }
createChinaBlurLine() { createChinaBlurLine() {
let geometry = new PlaneGeometry(147, 147) let geometry = new PlaneGeometry(147, 147);
const texture = this.assets.instance.getResource("chinaBlurLine") const texture = this.assets.instance.getResource("chinaBlurLine");
texture.colorSpace = SRGBColorSpace texture.colorSpace = SRGBColorSpace;
texture.wrapS = RepeatWrapping texture.wrapS = RepeatWrapping;
texture.wrapT = RepeatWrapping texture.wrapT = RepeatWrapping;
texture.generateMipmaps = false texture.generateMipmaps = false;
texture.minFilter = NearestFilter texture.minFilter = NearestFilter;
texture.repeat.set(1, 1) texture.repeat.set(1, 1);
let material = new MeshBasicMaterial({ let material = new MeshBasicMaterial({
color: 0x3f82cd, color: 0x3f82cd,
alphaMap: texture, alphaMap: texture,
transparent: true, transparent: true,
opacity: 0.5, opacity: 0.5,
}) });
let mesh = new Mesh(geometry, material) let mesh = new Mesh(geometry, material);
mesh.rotateX(-Math.PI / 2) mesh.rotateX(-Math.PI / 2);
mesh.position.set(-19.3, -0.5, -19.7) mesh.position.set(-19.3, -0.5, -19.7);
this.scene.add(mesh) this.scene.add(mesh);
} }
createLabel(val) { createLabel(val) {
let self = this let self = this;
let labelGroup = this.labelGroup let labelGroup = this.labelGroup;
let label3d = this.label3d let label3d = this.label3d;
let otherLabel = [] let otherLabel = [];
chinaData.map((province) => { chinaData.map((province) => {
if (province.hide == true) return false if (province.hide == true) return false;
let label = labelStyle01(province, label3d, labelGroup) let label = labelStyle01(province, label3d, labelGroup);
otherLabel.push(label) otherLabel.push(label);
}) });
this.mapFocusLabel = labelStyle02( this.mapFocusLabel = labelStyle02(
{ {
...val, ...val,
}, },
label3d, label3d,
labelGroup labelGroup
) );
let iconLabel1 = labelStyle03( let iconLabel1 = labelStyle03(
{ {
icon: labelIcon, icon: labelIcon,
...@@ -973,7 +1003,7 @@ export class World extends Mini3d { ...@@ -973,7 +1003,7 @@ export class World extends Mini3d {
}, },
label3d, label3d,
labelGroup labelGroup
) );
let iconLabel2 = labelStyle03( let iconLabel2 = labelStyle03(
{ {
icon: labelIcon, icon: labelIcon,
...@@ -984,47 +1014,55 @@ export class World extends Mini3d { ...@@ -984,47 +1014,55 @@ export class World extends Mini3d {
}, },
label3d, label3d,
labelGroup labelGroup
) );
otherLabel.push(this.mapFocusLabel) otherLabel.push(this.mapFocusLabel);
otherLabel.push(iconLabel1) otherLabel.push(iconLabel1);
otherLabel.push(iconLabel2) otherLabel.push(iconLabel2);
this.otherLabel = otherLabel this.otherLabel = otherLabel;
function labelStyle01(province, label3d, labelGroup) { function labelStyle01(province, label3d, labelGroup) {
let label = label3d.create("", `china-label ${province.blur ? " blur" : ""}`, false) let label = label3d.create(
const [x, y] = self.geoProjection(province.center) "",
`china-label ${province.blur ? " blur" : ""}`,
false
);
const [x, y] = self.geoProjection(province.center);
label.init( label.init(
`<div class="other-label"><img class="label-icon" src="${labelIcon}">${province.name}</div>`, `<div class="other-label"><img class="label-icon" src="${labelIcon}">${province.name}</div>`,
new Vector3(x, -y, 0.4) new Vector3(x, -y, 0.4)
) );
label3d.setLabelStyle(label, 0.02, "x") label3d.setLabelStyle(label, 0.02, "x");
label.setParent(labelGroup) label.setParent(labelGroup);
return label return label;
} }
function labelStyle02(province, label3d, labelGroup) { function labelStyle02(province, label3d, labelGroup) {
let label = label3d.create("", "map-label", false) let label = label3d.create("", "map-label", false);
const [x, y] = self.geoProjection(province.center) const [x, y] = self.geoProjection(province.center);
label.init( label.init(
`<div class="other-label"><span>${province.name}</span><span>${province.enName}</span></div>`, `<div class="other-label"><span>${province.name}</span><span>${province.enName}</span></div>`,
new Vector3(x, -y, 0.4) new Vector3(x, -y, 0.4)
) );
label3d.setLabelStyle(label, 0.015, "x") label3d.setLabelStyle(label, 0.015, "x");
label.setParent(labelGroup) label.setParent(labelGroup);
return label return label;
} }
function labelStyle03(data, label3d, labelGroup) { function labelStyle03(data, label3d, labelGroup) {
let label = label3d.create("", `decoration-label ${data.reflect ? " reflect" : ""}`, false) let label = label3d.create(
const [x, y] = self.geoProjection(data.center) "",
`decoration-label ${data.reflect ? " reflect" : ""}`,
false
);
const [x, y] = self.geoProjection(data.center);
label.init( label.init(
`<div class="other-label"><img class="label-icon" style="width:${data.width};height:${data.height}" src="${data.icon}">`, `<div class="other-label"><img class="label-icon" style="width:${data.width};height:${data.height}" src="${data.icon}">`,
new Vector3(x, -y, 0.4) new Vector3(x, -y, 0.4)
) );
label3d.setLabelStyle(label, 0.02, "x") label3d.setLabelStyle(label, 0.02, "x");
label.setParent(labelGroup) label.setParent(labelGroup);
return label return label;
} }
function labelStyle04(data, label3d, labelGroup, index) { function labelStyle04(data, label3d, labelGroup, index) {
let label = label3d.create("", "provinces-label", false) let label = label3d.create("", "provinces-label", false);
const [x, y] = self.geoProjection(data.center) const [x, y] = self.geoProjection(data.center);
label.init( label.init(
`<div class="provinces-label"> `<div class="provinces-label">
<div class="provinces-label-wrap"> <div class="provinces-label-wrap">
...@@ -1037,16 +1075,16 @@ export class World extends Mini3d { ...@@ -1037,16 +1075,16 @@ export class World extends Mini3d {
</div> </div>
</div>`, </div>`,
new Vector3(x, -y, 2.4) new Vector3(x, -y, 2.4)
) );
label3d.setLabelStyle(label, 0.02, "x") label3d.setLabelStyle(label, 0.02, "x");
label.setParent(labelGroup) label.setParent(labelGroup);
return label return label;
} }
} }
createRotateBorder() { createRotateBorder() {
let max = 12 let max = 12;
let rotationBorder1 = this.assets.instance.getResource("rotationBorder1") let rotationBorder1 = this.assets.instance.getResource("rotationBorder1");
let rotationBorder2 = this.assets.instance.getResource("rotationBorder2") let rotationBorder2 = this.assets.instance.getResource("rotationBorder2");
let plane01 = new Plane(this, { let plane01 = new Plane(this, {
width: max * 1.178, width: max * 1.178,
needRotate: true, needRotate: true,
...@@ -1061,11 +1099,11 @@ export class World extends Mini3d { ...@@ -1061,11 +1099,11 @@ export class World extends Mini3d {
blending: AdditiveBlending, blending: AdditiveBlending,
}), }),
position: new Vector3(0, 0.28, 0), position: new Vector3(0, 0.28, 0),
}) });
plane01.instance.rotation.x = -Math.PI / 2 plane01.instance.rotation.x = -Math.PI / 2;
plane01.instance.renderOrder = 6 plane01.instance.renderOrder = 6;
plane01.instance.scale.set(0, 0, 0) plane01.instance.scale.set(0, 0, 0);
plane01.setParent(this.scene) plane01.setParent(this.scene);
let plane02 = new Plane(this, { let plane02 = new Plane(this, {
width: max * 1.116, width: max * 1.116,
needRotate: true, needRotate: true,
...@@ -1080,27 +1118,27 @@ export class World extends Mini3d { ...@@ -1080,27 +1118,27 @@ export class World extends Mini3d {
blending: AdditiveBlending, blending: AdditiveBlending,
}), }),
position: new Vector3(0, 0.3, 0), position: new Vector3(0, 0.3, 0),
}) });
plane02.instance.rotation.x = -Math.PI / 2 plane02.instance.rotation.x = -Math.PI / 2;
plane02.instance.renderOrder = 6 plane02.instance.renderOrder = 6;
plane02.instance.scale.set(0, 0, 0) plane02.instance.scale.set(0, 0, 0);
plane02.setParent(this.scene) plane02.setParent(this.scene);
this.rotateBorder1 = plane01.instance this.rotateBorder1 = plane01.instance;
this.rotateBorder2 = plane02.instance this.rotateBorder2 = plane02.instance;
} }
createFlyLine() { createFlyLine() {
this.flyLineGroup = new Group() this.flyLineGroup = new Group();
this.flyLineGroup.visible = false this.flyLineGroup.visible = false;
this.scene.add(this.flyLineGroup) this.scene.add(this.flyLineGroup);
const texture = this.assets.instance.getResource("mapFlyline") const texture = this.assets.instance.getResource("mapFlyline");
texture.wrapS = texture.wrapT = RepeatWrapping texture.wrapS = texture.wrapT = RepeatWrapping;
texture.repeat.set(0.5, 2) texture.repeat.set(0.5, 2);
const tubeRadius = 0.1 const tubeRadius = 0.1;
const tubeSegments = 32 const tubeSegments = 32;
const tubeRadialSegments = 2 const tubeRadialSegments = 2;
const closed = false const closed = false;
let [centerX, centerY] = this.geoProjection(this.flyLineCenter) let [centerX, centerY] = this.geoProjection(this.flyLineCenter);
let centerPoint = new Vector3(centerX, -centerY, 0) let centerPoint = new Vector3(centerX, -centerY, 0);
const material = new MeshBasicMaterial({ const material = new MeshBasicMaterial({
map: texture, map: texture,
// alphaMap: texture, // alphaMap: texture,
...@@ -1110,34 +1148,40 @@ export class World extends Mini3d { ...@@ -1110,34 +1148,40 @@ export class World extends Mini3d {
opacity: 1, opacity: 1,
depthTest: false, depthTest: false,
blending: AdditiveBlending, blending: AdditiveBlending,
}) });
this.time.on("tick", () => { this.time.on("tick", () => {
texture.offset.x -= 0.006 texture.offset.x -= 0.006;
}) });
provincesData provincesData
.filter((item, index) => index < 7) .filter((item, index) => index < 7)
.map((city) => { .map((city) => {
let [x, y] = this.geoProjection(city.centroid) let [x, y] = this.geoProjection(city.centroid);
let point = new Vector3(x, -y, 0) let point = new Vector3(x, -y, 0);
const center = new Vector3() const center = new Vector3();
center.addVectors(centerPoint, point).multiplyScalar(0.5) center.addVectors(centerPoint, point).multiplyScalar(0.5);
center.setZ(3) center.setZ(3);
const curve = new QuadraticBezierCurve3(centerPoint, center, point) const curve = new QuadraticBezierCurve3(centerPoint, center, point);
const tubeGeometry = new TubeGeometry(curve, tubeSegments, tubeRadius, tubeRadialSegments, closed) const tubeGeometry = new TubeGeometry(
const mesh = new Mesh(tubeGeometry, material) curve,
mesh.rotation.x = -Math.PI / 2 tubeSegments,
mesh.position.set(0, this.depth + 0.44, 0) tubeRadius,
mesh.renderOrder = 21 tubeRadialSegments,
this.flyLineGroup.add(mesh) closed
}) );
const mesh = new Mesh(tubeGeometry, material);
mesh.rotation.x = -Math.PI / 2;
mesh.position.set(0, this.depth + 0.44, 0);
mesh.renderOrder = 21;
this.flyLineGroup.add(mesh);
});
} }
// 创建焦点 // 创建焦点
createFocus() { createFocus() {
let focusObj = new Focus(this, { color1: 0xbdfdfd, color2: 0xbdfdfd }) let focusObj = new Focus(this, { color1: 0xbdfdfd, color2: 0xbdfdfd });
let [x, y] = this.geoProjection(this.flyLineCenter) let [x, y] = this.geoProjection(this.flyLineCenter);
focusObj.position.set(x, -y, this.depth + 0.44) focusObj.position.set(x, -y, this.depth + 0.44);
focusObj.scale.set(1, 1, 1) focusObj.scale.set(1, 1, 1);
this.flyLineFocusGroup.add(focusObj) this.flyLineFocusGroup.add(focusObj);
} }
// 创建粒子 // 创建粒子
createParticles() { createParticles() {
...@@ -1158,54 +1202,54 @@ export class World extends Mini3d { ...@@ -1158,54 +1202,54 @@ export class World extends Mini3d {
blending: AdditiveBlending, blending: AdditiveBlending,
sizeAttenuation: true, sizeAttenuation: true,
}), }),
}) });
this.particleGroup = new Group() this.particleGroup = new Group();
this.scene.add(this.particleGroup) this.scene.add(this.particleGroup);
this.particleGroup.rotation.x = -Math.PI / 2 this.particleGroup.rotation.x = -Math.PI / 2;
this.particles.setParent(this.particleGroup) this.particles.setParent(this.particleGroup);
this.particles.enable = true this.particles.enable = true;
this.particleGroup.visible = true this.particleGroup.visible = true;
} }
createScatter() { createScatter() {
this.scatterGroup = new Group() this.scatterGroup = new Group();
this.scatterGroup.visible = false this.scatterGroup.visible = false;
this.scatterGroup.rotation.x = -Math.PI / 2 this.scatterGroup.rotation.x = -Math.PI / 2;
this.scene.add(this.scatterGroup) this.scene.add(this.scatterGroup);
const texture = this.assets.instance.getResource("arrow") const texture = this.assets.instance.getResource("arrow");
const material = new SpriteMaterial({ const material = new SpriteMaterial({
map: texture, map: texture,
color: 0xfffef4, color: 0xfffef4,
fog: false, fog: false,
transparent: true, transparent: true,
depthTest: false, depthTest: false,
}) });
let scatterAllData = sortByValue(scatterData) let scatterAllData = sortByValue(scatterData);
let max = scatterAllData[0].value let max = scatterAllData[0].value;
scatterAllData.map((data) => { scatterAllData.map((data) => {
const sprite = new Sprite(material) const sprite = new Sprite(material);
sprite.renderOrder = 23 sprite.renderOrder = 23;
let scale = 0.1 + (data.value / max) * 0.2 let scale = 0.1 + (data.value / max) * 0.2;
sprite.scale.set(scale, scale, scale) sprite.scale.set(scale, scale, scale);
let [x, y] = this.geoProjection([data.lng, data.lat]) let [x, y] = this.geoProjection([data.lng, data.lat]);
sprite.position.set(x, -y, this.depth + 0.45) sprite.position.set(x, -y, this.depth + 0.45);
sprite.userData.position = [x, -y, this.depth + 0.45] sprite.userData.position = [x, -y, this.depth + 0.45];
this.scatterGroup.add(sprite) this.scatterGroup.add(sprite);
}) });
} }
createInfoPoint() { createInfoPoint() {
let self = this let self = this;
this.InfoPointGroup = new Group() this.InfoPointGroup = new Group();
this.scene.add(this.InfoPointGroup) this.scene.add(this.InfoPointGroup);
this.InfoPointGroup.visible = false this.InfoPointGroup.visible = false;
this.InfoPointGroup.rotation.x = -Math.PI / 2 this.InfoPointGroup.rotation.x = -Math.PI / 2;
this.infoPointIndex = 0 this.infoPointIndex = 0;
this.infoPointLabelTime = null this.infoPointLabelTime = null;
this.infoLabelElement = [] this.infoLabelElement = [];
let label3d = this.label3d let label3d = this.label3d;
const texture = this.assets.instance.getResource("point") const texture = this.assets.instance.getResource("point");
let colors = [0xfffef4, 0x77fbf5] let colors = [0xfffef4, 0x77fbf5];
let infoAllData = sortByValue(infoData) let infoAllData = sortByValue(infoData);
let max = infoAllData[0].value let max = infoAllData[0].value;
infoAllData.map((data, index) => { infoAllData.map((data, index) => {
const material = new SpriteMaterial({ const material = new SpriteMaterial({
map: texture, map: texture,
...@@ -1213,49 +1257,49 @@ export class World extends Mini3d { ...@@ -1213,49 +1257,49 @@ export class World extends Mini3d {
fog: false, fog: false,
transparent: true, transparent: true,
depthTest: false, depthTest: false,
}) });
const sprite = new Sprite(material) const sprite = new Sprite(material);
sprite.renderOrder = 23 sprite.renderOrder = 23;
let scale = 0.7 + (data.value / max) * 0.4 let scale = 0.7 + (data.value / max) * 0.4;
sprite.scale.set(scale, scale, scale) sprite.scale.set(scale, scale, scale);
let [x, y] = this.geoProjection([data.lng, data.lat]) let [x, y] = this.geoProjection([data.lng, data.lat]);
let position = [x, -y, this.depth + 0.7] let position = [x, -y, this.depth + 0.7];
sprite.position.set(...position) sprite.position.set(...position);
sprite.userData.position = [...position] sprite.userData.position = [...position];
sprite.userData = { sprite.userData = {
position: [x, -y, this.depth + 0.7], position: [x, -y, this.depth + 0.7],
name: data.name, name: data.name,
value: data.value, value: data.value,
level: data.level, level: data.level,
index: index, index: index,
} };
this.InfoPointGroup.add(sprite) this.InfoPointGroup.add(sprite);
let label = infoLabel(data, label3d, this.InfoPointGroup) let label = infoLabel(data, label3d, this.InfoPointGroup);
this.infoLabelElement.push(label) this.infoLabelElement.push(label);
this.interactionManager.add(sprite) this.interactionManager.add(sprite);
sprite.addEventListener("mousedown", (ev) => { sprite.addEventListener("mousedown", (ev) => {
if (this.clicked || !this.InfoPointGroup.visible) return false if (this.clicked || !this.InfoPointGroup.visible) return false;
this.clicked = true this.clicked = true;
this.infoPointIndex = ev.target.userData.index this.infoPointIndex = ev.target.userData.index;
this.infoLabelElement.map((label) => { this.infoLabelElement.map((label) => {
label.visible = false label.visible = false;
}) });
label.visible = true label.visible = true;
this.createInfoPointLabelLoop() this.createInfoPointLabelLoop();
}) });
sprite.addEventListener("mouseup", (ev) => { sprite.addEventListener("mouseup", (ev) => {
this.clicked = false this.clicked = false;
}) });
sprite.addEventListener("mouseover", (event) => { sprite.addEventListener("mouseover", (event) => {
document.body.style.cursor = "pointer" document.body.style.cursor = "pointer";
}) });
sprite.addEventListener("mouseout", (event) => { sprite.addEventListener("mouseout", (event) => {
document.body.style.cursor = "default" document.body.style.cursor = "default";
}) });
}) });
function infoLabel(data, label3d, labelGroup) { function infoLabel(data, label3d, labelGroup) {
let label = label3d.create("", "info-point", true) let label = label3d.create("", "info-point", true);
const [x, y] = self.geoProjection([data.lng, data.lat]) const [x, y] = self.geoProjection([data.lng, data.lat]);
label.init( label.init(
` <div class="info-point-wrap"> ` <div class="info-point-wrap">
<div class="info-point-wrap-inner"> <div class="info-point-wrap-inner">
...@@ -1273,34 +1317,34 @@ export class World extends Mini3d { ...@@ -1273,34 +1317,34 @@ export class World extends Mini3d {
</div> </div>
`, `,
new Vector3(x, -y, self.depth + 1.9) new Vector3(x, -y, self.depth + 1.9)
) );
label3d.setLabelStyle(label, 0.015, "x") label3d.setLabelStyle(label, 0.015, "x");
label.setParent(labelGroup) label.setParent(labelGroup);
label.visible = false label.visible = false;
return label return label;
} }
} }
createInfoPointLabelLoop() { createInfoPointLabelLoop() {
clearInterval(this.infoPointLabelTime) clearInterval(this.infoPointLabelTime);
this.infoPointLabelTime = setInterval(() => { this.infoPointLabelTime = setInterval(() => {
this.infoPointIndex++ this.infoPointIndex++;
if (this.infoPointIndex >= this.infoLabelElement.length) { if (this.infoPointIndex >= this.infoLabelElement.length) {
this.infoPointIndex = 0 this.infoPointIndex = 0;
} }
this.infoLabelElement.map((label, i) => { this.infoLabelElement.map((label, i) => {
if (this.infoPointIndex === i) { if (this.infoPointIndex === i) {
label.visible = true label.visible = true;
} else { } else {
label.visible = false label.visible = false;
} }
}) });
}, 3000) }, 3000);
} }
createStorke() { createStorke() {
const mapStroke = this.assets.instance.getResource("mapStroke") const mapStroke = this.assets.instance.getResource("mapStroke");
const texture = this.assets.instance.getResource("pathLine3") const texture = this.assets.instance.getResource("pathLine3");
texture.wrapS = texture.wrapT = RepeatWrapping texture.wrapS = texture.wrapT = RepeatWrapping;
texture.repeat.set(2, 1) texture.repeat.set(2, 1);
let pathLine = new Line(this, { let pathLine = new Line(this, {
geoProjectionCenter: this.geoProjectionCenter, geoProjectionCenter: this.geoProjectionCenter,
...@@ -1319,18 +1363,21 @@ export class World extends Mini3d { ...@@ -1319,18 +1363,21 @@ export class World extends Mini3d {
type: "Line3", type: "Line3",
renderOrder: 22, renderOrder: 22,
tubeRadius: 0.03, tubeRadius: 0.03,
}) });
// 设置父级 // 设置父级
this.focusMapGroup.add(pathLine.lineGroup) this.focusMapGroup.add(pathLine.lineGroup);
this.time.on("tick", () => { this.time.on("tick", () => {
texture.offset.x += 0.005 texture.offset.x += 0.005;
}) });
} }
// 创建地形模型 // 创建地形模型
createTerrain() { createTerrain() {
this.pngTexture = this.terrainResource.loaders.Texture.load("../../../public/assets/json/TrueColorImage.png"); this.pngTexture = this.terrainResource.loaders.Texture.load(
"../../../public/assets/json/TrueColorImage.png"
);
this.pngTexture.flipY = false; this.pngTexture.flipY = false;
this.terrainResource.loaders.GLTF.load("../../../public/assets/json/Scene_cq15_200.gltf", this.terrainResource.loaders.GLTF.load(
"../../../public/assets/json/Scene_cq15_200.gltf",
(gltf) => { (gltf) => {
let terrain = gltf.scene; let terrain = gltf.scene;
// 调整 GLTF 模型的缩放比例和位置 // 调整 GLTF 模型的缩放比例和位置
...@@ -1351,149 +1398,151 @@ export class World extends Mini3d { ...@@ -1351,149 +1398,151 @@ export class World extends Mini3d {
this.scene.add(terrain); this.scene.add(terrain);
}, },
(xhr) => { (xhr) => {
console.log((xhr.loaded / xhr.total * 100) + '% 已加载'); console.log((xhr.loaded / xhr.total) * 100 + "% 已加载");
}, },
(error) => { (error) => {
console.error('加载模型出错:', error); console.error("加载模型出错:", error);
} }
) );
// this.scene.add(barGroup) // this.scene.add(barGroup)
} }
// 创建geoJson // 创建geoJson
createGeoJson() { createGeoJson() {
this.FileLoader = new FileLoader() this.FileLoader = new FileLoader();
this.FileLoader.load("../../../public/assets/json/surfacewater.json", (data) => { this.FileLoader.load(
let jsonData = JSON.parse(data) "../../../public/assets/json/surfacewater.json",
(data) => {
let jsonData = JSON.parse(data);
console.log("jsonData", jsonData); console.log("jsonData", jsonData);
const roadGeoJson = new Group(); const roadGeoJson = new Group();
const roadProjection = geoMercator().center(this.geoProjectionCenter) const roadProjection = geoMercator().center(this.geoProjectionCenter);
// 遍历Json的feature // 遍历Json的feature
jsonData.features.forEach((feature,i)=>{ jsonData.features.forEach((feature, i) => {
// 获取feature中的geometry数据,方便引用 // 获取feature中的geometry数据,方便引用
const geometry = feature.geometry; const geometry = feature.geometry;
const type = geometry.type; const type = geometry.type;
// 创建分组,把同一个市的形状放在一个分组,便于管理 // 创建分组,把同一个市的形状放在一个分组,便于管理
const city = new Group(); const city = new Group();
if(type === 'LineString'){ if (type === "LineString") {
// 遍历geometry中的数据 // 遍历geometry中的数据
geometry.coordinates.forEach((multipolygon)=>{ geometry.coordinates.forEach((multipolygon) => {
// 创建形状,用于展示地理形状 // 创建形状,用于展示地理形状
const shape = new Shape() const shape = new Shape();
// 存储每个坐标,用于绘制边界线 // 存储每个坐标,用于绘制边界线
const arr = [] const arr = [];
multipolygon.forEach((polygon)=>{ multipolygon.forEach((polygon) => {
polygon.forEach((item,index) => { polygon.forEach((item, index) => {
// 使用d3转换坐标 // 使用d3转换坐标
const [x,y]= roadProjection.translate([0,0])(item); const [x, y] = roadProjection.translate([0, 0])(item);
// 根据转换后的坐标绘制形状 // 根据转换后的坐标绘制形状
if(index === 0){ if (index === 0) {
shape.moveTo(x,y) shape.moveTo(x, y);
}else { } else {
shape.lineTo(x,y) shape.lineTo(x, y);
} }
arr.push(x,y,3) arr.push(x, y, 3);
});
}); });
})
console.log("arr", arr); console.log("arr", arr);
// 绘制边界线 // 绘制边界线
createLine(arr,city) createLine(arr, city);
// 创建 ExtrudeGeometry用于显示3d效果 // 创建 ExtrudeGeometry用于显示3d效果
const extrudGeometry = new ExtrudeGeometry(shape,{ const extrudGeometry = new ExtrudeGeometry(shape, {
depth:2 // 地图的厚度 depth: 2, // 地图的厚度
}) });
// 创建材质,用于显示地图的外观 // 创建材质,用于显示地图的外观
const material = new MeshBasicMaterial({ const material = new MeshBasicMaterial({
color:'#ffff33' color: "#ffff33",
}); });
// 创建物体,用于显示地图 // 创建物体,用于显示地图
const mesh = new Mesh(extrudGeometry,material) const mesh = new Mesh(extrudGeometry, material);
mesh.position.set(0,0,0) mesh.position.set(0, 0, 0);
// 添加到分组 // 添加到分组
city.add(mesh) city.add(mesh);
}) });
} }
// 添加到分组 // 添加到分组
roadGeoJson.add(city) roadGeoJson.add(city);
}) });
this.scene.add(roadGeoJson); this.scene.add(roadGeoJson);
}) }
);
} }
// createSmoothLine(lngLatArr) {
// const z = this.depth + 0.5;
// const points = lngLatArr.map(([lng, lat]) => {
// const [x, y] = this.geoProjection([lng, lat]);
// return new THREE.Vector3(x, z, y);
// });
// const curve = new THREE.CatmullRomCurve3(points, false, 'catmullrom', 0.5);
// const curvePoints = curve.getPoints(100);
// createSmoothLine(lngLatArr) { // // 计算每个点的累积距离
// const z = this.depth + 0.5; // let distances = [0];
// const points = lngLatArr.map(([lng, lat]) => { // for (let i = 1; i < curvePoints.length; i++) {
// const [x, y] = this.geoProjection([lng, lat]); // distances[i] = distances[i - 1] + curvePoints[i].distanceTo(curvePoints[i - 1]);
// return new THREE.Vector3(x, z, y); // }
// }); // const totalLength = distances[distances.length - 1];
// const curve = new THREE.CatmullRomCurve3(points, false, 'catmullrom', 0.5);
// const curvePoints = curve.getPoints(100);
// // 计算每个点的累积距离
// let distances = [0];
// for (let i = 1; i < curvePoints.length; i++) {
// distances[i] = distances[i - 1] + curvePoints[i].distanceTo(curvePoints[i - 1]);
// }
// const totalLength = distances[distances.length - 1];
// // 添加距离属性 // // 添加距离属性
// const geometry = new THREE.BufferGeometry().setFromPoints(curvePoints); // const geometry = new THREE.BufferGeometry().setFromPoints(curvePoints);
// const distanceAttr = new Float32Array(distances); // const distanceAttr = new Float32Array(distances);
// geometry.setAttribute('aDistance', new THREE.BufferAttribute(distanceAttr, 1)); // geometry.setAttribute('aDistance', new THREE.BufferAttribute(distanceAttr, 1));
// // ShaderMaterial // // ShaderMaterial
// const material = new THREE.ShaderMaterial({ // const material = new THREE.ShaderMaterial({
// uniforms: { // uniforms: {
// color: { value: new THREE.Color(0xff0000) }, // color: { value: new THREE.Color(0xff0000) },
// dashSize: { value: 0.2 }, // dashSize: { value: 0.2 },
// gapSize: { value: 0.1 }, // gapSize: { value: 0.1 },
// totalLength: { value: totalLength }, // totalLength: { value: totalLength },
// dashOffset: { value: 0 }, // dashOffset: { value: 0 },
// }, // },
// vertexShader: ` // vertexShader: `
// attribute float aDistance; // attribute float aDistance;
// varying float vDistance; // varying float vDistance;
// void main() { // void main() {
// vDistance = aDistance; // vDistance = aDistance;
// gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); // gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
// } // }
// `, // `,
// fragmentShader: ` // fragmentShader: `
// uniform vec3 color; // uniform vec3 color;
// uniform float dashSize; // uniform float dashSize;
// uniform float gapSize; // uniform float gapSize;
// uniform float totalLength; // uniform float totalLength;
// uniform float dashOffset; // uniform float dashOffset;
// varying float vDistance; // varying float vDistance;
// void main() { // void main() {
// float unitLen = dashSize + gapSize; // float unitLen = dashSize + gapSize;
// float dist = mod(vDistance + dashOffset * totalLength, unitLen); // float dist = mod(vDistance + dashOffset * totalLength, unitLen);
// if(dist > dashSize) discard; // if(dist > dashSize) discard;
// gl_FragColor = vec4(color, 1.0); // gl_FragColor = vec4(color, 1.0);
// } // }
// `, // `,
// transparent: true // transparent: true
// }); // });
// const line = new THREE.Line(geometry, material); // const line = new THREE.Line(geometry, material);
// this.scene.add(line); // this.scene.add(line);
// // 动画 // // 动画
// this.time.on("tick", () => { // this.time.on("tick", () => {
// material.uniforms.dashOffset.value += 0.001; // 控制流动速度 // material.uniforms.dashOffset.value += 0.001; // 控制流动速度
// if(material.uniforms.dashOffset.value > 1.0) material.uniforms.dashOffset.value -= 1.0; // if(material.uniforms.dashOffset.value > 1.0) material.uniforms.dashOffset.value -= 1.0;
// }); // });
// return line; // return line;
// } // }
createSmoothLine(lngLatArr) { createSmoothLine(lngLatArr) {
const z = this.depth + 0.5; const z = this.depth + 0.5;
// 生成原始曲线 // 生成原始曲线
const points = lngLatArr.map(([lng, lat]) => { const points = lngLatArr.map(([lng, lat]) => {
const [x, y] = this.geoProjection([lng, lat]); const [x, y] = this.geoProjection([lng, lat]);
return new THREE.Vector3(x, z, y); return new THREE.Vector3(x, z, y);
}); });
const curve = new THREE.CatmullRomCurve3(points, false, 'catmullrom', 0.5); const curve = new THREE.CatmullRomCurve3(points, false, "catmullrom", 0.5);
// 创建管道几何体(控制宽度) // 创建管道几何体(控制宽度)
const radius = 0.05; // 控制线条宽度 const radius = 0.05; // 控制线条宽度
const radialSegments = 8; // 控制管道圆滑度 const radialSegments = 8; // 控制管道圆滑度
...@@ -1544,7 +1593,7 @@ createSmoothLine(lngLatArr) { ...@@ -1544,7 +1593,7 @@ createSmoothLine(lngLatArr) {
gl_FragColor = vec4(color, 1.0); gl_FragColor = vec4(color, 1.0);
} }
`, `,
transparent: true transparent: true,
}); });
// 创建网格对象 // 创建网格对象
const mesh = new THREE.Mesh(tubeGeometry, material); const mesh = new THREE.Mesh(tubeGeometry, material);
...@@ -1553,31 +1602,37 @@ createSmoothLine(lngLatArr) { ...@@ -1553,31 +1602,37 @@ createSmoothLine(lngLatArr) {
// 保持原有动画逻辑 // 保持原有动画逻辑
this.time.on("tick", () => { this.time.on("tick", () => {
material.uniforms.dashOffset.value += 0.001; material.uniforms.dashOffset.value += 0.001;
if(material.uniforms.dashOffset.value > 1.0) { if (material.uniforms.dashOffset.value > 1.0) {
material.uniforms.dashOffset.value -= 1.0; material.uniforms.dashOffset.value -= 1.0;
} }
}); });
return mesh; return mesh;
} }
// 创建水面 // 创建水面
createWater() { createWater() {
// 获取水面GeoJSON数据 // 获取水面GeoJSON数据
let mapJsonData = this.assets.instance.getResource("surfaceWater"); let mapJsonData = this.assets.instance.getResource("surfaceWater");
let waterJson; let waterJson;
try { try {
waterJson = typeof mapJsonData === "string" ? JSON.parse(mapJsonData) : mapJsonData; waterJson =
typeof mapJsonData === "string" ? JSON.parse(mapJsonData) : mapJsonData;
} catch (e) { } catch (e) {
console.error("surfaceWater 解析失败", e); console.error("surfaceWater 解析失败", e);
return { waterGroup: null }; return { waterGroup: null };
} }
// 创建水面组 // 创建水面组
const waterGroup = new Group(); const waterGroup = new Group();
const projection = geoMercator().center(this.geoProjectionCenter).scale(this.geoProjectionScale).translate([0, 0]); const projection = geoMercator()
.center(this.geoProjectionCenter)
.scale(this.geoProjectionScale)
.translate([0, 0]);
// 加载法线贴图 // 加载法线贴图
const textureLoader = new THREE.TextureLoader(); const textureLoader = new THREE.TextureLoader();
let base_url = import.meta.env.BASE_URL || "/"; let base_url = import.meta.env.BASE_URL || "/";
const normalMap = textureLoader.load(base_url + "assets/json/waternormals.jpg"); const normalMap = textureLoader.load(
base_url + "assets/json/waternormals.jpg"
);
normalMap.wrapS = normalMap.wrapT = THREE.RepeatWrapping; normalMap.wrapS = normalMap.wrapT = THREE.RepeatWrapping;
// 可选:加载一张水面颜色贴图 // 可选:加载一张水面颜色贴图
// const waterMap = textureLoader.load(base_url + "assets/json/watercolor.png"); // const waterMap = textureLoader.load(base_url + "assets/json/watercolor.png");
...@@ -1595,7 +1650,10 @@ createWater() { ...@@ -1595,7 +1650,10 @@ createWater() {
const geometry = feature.geometry; const geometry = feature.geometry;
if (!geometry) return; if (!geometry) return;
if (geometry.type === "Polygon" || geometry.type === "MultiPolygon") { if (geometry.type === "Polygon" || geometry.type === "MultiPolygon") {
const polygons = geometry.type === "Polygon" ? [geometry.coordinates] : geometry.coordinates; const polygons =
geometry.type === "Polygon"
? [geometry.coordinates]
: geometry.coordinates;
polygons.forEach((polygon) => { polygons.forEach((polygon) => {
polygon.forEach((ring) => { polygon.forEach((ring) => {
const shape = new Shape(); const shape = new Shape();
...@@ -1631,13 +1689,14 @@ createWater() { ...@@ -1631,13 +1689,14 @@ createWater() {
return { return {
waterGroup, waterGroup,
}; };
} }
createLineWater() { createLineWater() {
// 获取水的GeoJSON线数据 // 获取水的GeoJSON线数据
let mapJsonData = this.assets.instance.getResource("linewater"); let mapJsonData = this.assets.instance.getResource("linewater");
let lineWaterJson; let lineWaterJson;
try { try {
lineWaterJson = typeof mapJsonData === "string" ? JSON.parse(mapJsonData) : mapJsonData; lineWaterJson =
typeof mapJsonData === "string" ? JSON.parse(mapJsonData) : mapJsonData;
} catch (e) { } catch (e) {
console.error("linewater 解析失败", e); console.error("linewater 解析失败", e);
return { lineWaterGroup: null }; return { lineWaterGroup: null };
...@@ -1646,7 +1705,9 @@ createLineWater() { ...@@ -1646,7 +1705,9 @@ createLineWater() {
// 加载法线贴图 // 加载法线贴图
const textureLoader = new THREE.TextureLoader(); const textureLoader = new THREE.TextureLoader();
let base_url = import.meta.env.BASE_URL || "/"; let base_url = import.meta.env.BASE_URL || "/";
const normalMap = textureLoader.load(base_url + "assets/json/waternormals.jpg"); const normalMap = textureLoader.load(
base_url + "assets/json/waternormals.jpg"
);
normalMap.wrapS = normalMap.wrapT = THREE.RepeatWrapping; normalMap.wrapS = normalMap.wrapT = THREE.RepeatWrapping;
// 可选:加载一张水面颜色贴图 // 可选:加载一张水面颜色贴图
// const waterMap = textureLoader.load(base_url + "assets/json/watercolor.png"); // const waterMap = textureLoader.load(base_url + "assets/json/watercolor.png");
...@@ -1662,7 +1723,10 @@ createLineWater() { ...@@ -1662,7 +1723,10 @@ createLineWater() {
// 创建线水组 // 创建线水组
const lineWaterGroup = new Group(); const lineWaterGroup = new Group();
const projection = geoMercator().center(this.geoProjectionCenter).scale(this.geoProjectionScale).translate([0, 0]); const projection = geoMercator()
.center(this.geoProjectionCenter)
.scale(this.geoProjectionScale)
.translate([0, 0]);
(lineWaterJson.features || []).forEach((feature) => { (lineWaterJson.features || []).forEach((feature) => {
const geometry = feature.geometry; const geometry = feature.geometry;
if (!geometry) return; if (!geometry) return;
...@@ -1723,229 +1787,454 @@ createLineWater() { ...@@ -1723,229 +1787,454 @@ createLineWater() {
return { return {
lineWaterGroup, lineWaterGroup,
}; };
} }
// createLineWater() {
// // 获取水的GeoJSON线数据
// let mapJsonData = this.assets.instance.getResource("linewater");
// let lineWaterJson;
// try {
// lineWaterJson = typeof mapJsonData === "string" ? JSON.parse(mapJsonData) : mapJsonData;
// } catch (e) {
// console.error("linewater 解析失败", e);
// return { lineWaterGroup: null };
// }
// // 加载法线贴图
// const textureLoader = new THREE.TextureLoader();
// let base_url = import.meta.env.BASE_URL || "/";
// const normalMap = textureLoader.load(base_url + "assets/json/waternormals.jpg");
// normalMap.wrapS = normalMap.wrapT = THREE.RepeatWrapping;
// const waterMap = textureLoader.load(base_url + "assets/json/watercolor.png"); // 你需要准备一张水波纹PNG
// waterMap.wrapS = waterMap.wrapT = THREE.RepeatWrapping;
// // 动画流动
// this.time.on("tick", () => {
// normalMap.offset.x += 0.002;
// normalMap.offset.y += 0.001;
// waterMap.offset.x += 0.002; // 让颜色贴图也流动
// });
// // 创建线水组
// const lineWaterGroup = new Group();
// const projection = geoMercator().center(this.geoProjectionCenter).scale(this.geoProjectionScale).translate([0, 0]);
// (lineWaterJson.features || []).forEach((feature) => {
// const geometry = feature.geometry;
// if (!geometry) return;
// let lines = [];
// if (geometry.type === "LineString") {
// lines = [geometry.coordinates];
// } else if (geometry.type === "MultiLineString") {
// lines = geometry.coordinates;
// }
// lines.forEach((line) => {
// // 生成带宽度的水带
// const points = line.map(([lng, lat]) => {
// const [x, y] = projection([lng, lat]);
// return new THREE.Vector3(x, -y, this.depth + 0.26); // 保证高于地表
// });
// if (points.length < 2) return;
// // 用TubeGeometry生成带宽度的水带
// const curve = new THREE.CatmullRomCurve3(points);
// const tubeGeometry = new THREE.TubeGeometry(curve, Math.max(32, points.length * 2), 0.01, 16);
// const material = new THREE.MeshStandardMaterial({
// color: 0x4fc3ff,
// transparent: true,
// opacity: 0.35,
// side: DoubleSide,
// depthWrite: false,
// normalMap: normalMap,
// normalScale: new THREE.Vector2(2, 2),
// map: waterMap, // 使用颜色贴图
// metalness: 0.3,
// roughness: 0.7,
// });
// const mesh = new Mesh(tubeGeometry, material);
// mesh.renderOrder = 11;
// lineWaterGroup.add(mesh);
// });
// });
// return {
// lineWaterGroup,
// };
// }
// createLineWater() {
// let mapJsonData = this.assets.instance.getResource("linewater");
// let lineWaterJson;
// try {
// lineWaterJson = typeof mapJsonData === "string" ? JSON.parse(mapJsonData) : mapJsonData;
// } catch (e) {
// console.error("linewater 解析失败", e);
// return { lineWaterGroup: null };
// }
// const textureLoader = new THREE.TextureLoader();
// let base_url = import.meta.env.BASE_URL || "/";
// const normalMap = textureLoader.load(base_url + "assets/json/waternormals.jpg");
// normalMap.wrapS = normalMap.wrapT = THREE.RepeatWrapping;
// const waterMap = textureLoader.load(base_url + "assets/json/watercolor.png");
// waterMap.wrapS = waterMap.wrapT = THREE.RepeatWrapping;
// // 流动速度更快
// this.time.on("tick", () => {
// normalMap.offset.x += 0.03;
// normalMap.offset.y += 0.015;
// waterMap.offset.x += 0.03;
// waterMap.offset.y += 0.015;
// });
// createLineWater() { // const lineWaterGroup = new Group();
// // 获取水的GeoJSON线数据 // const projection = geoMercator().center(this.geoProjectionCenter).scale(this.geoProjectionScale).translate([0, 0]);
// let mapJsonData = this.assets.instance.getResource("linewater");
// let lineWaterJson;
// try {
// lineWaterJson = typeof mapJsonData === "string" ? JSON.parse(mapJsonData) : mapJsonData;
// } catch (e) {
// console.error("linewater 解析失败", e);
// return { lineWaterGroup: null };
// }
// // 加载法线贴图 // (lineWaterJson.features || []).forEach((feature) => {
// const textureLoader = new THREE.TextureLoader(); // const geometry = feature.geometry;
// let base_url = import.meta.env.BASE_URL || "/"; // if (!geometry) return;
// const normalMap = textureLoader.load(base_url + "assets/json/waternormals.jpg"); // let lines = [];
// normalMap.wrapS = normalMap.wrapT = THREE.RepeatWrapping; // if (geometry.type === "LineString") {
// const waterMap = textureLoader.load(base_url + "assets/json/watercolor.png"); // 你需要准备一张水波纹PNG // lines = [geometry.coordinates];
// waterMap.wrapS = waterMap.wrapT = THREE.RepeatWrapping; // } else if (geometry.type === "MultiLineString") {
// // 动画流动 // lines = geometry.coordinates;
// this.time.on("tick", () => { // }
// normalMap.offset.x += 0.002; // lines.forEach((line) => {
// normalMap.offset.y += 0.001; // const points = line.map(([lng, lat]) => {
// waterMap.offset.x += 0.002; // 让颜色贴图也流动 // const [x, y] = projection([lng, lat]);
// }); // return new THREE.Vector3(x, -y, this.depth + 0.26);
// });
// if (points.length < 2) return;
// // 创建线水组 // // 宽度渐变:首尾变窄
// const lineWaterGroup = new Group(); // const riverWidthMax = 0.08, riverWidthMin = 0.02;
// const projection = geoMercator().center(this.geoProjectionCenter).scale(this.geoProjectionScale).translate([0, 0]); // const lefts = [], rights = [];
// for (let i = 0; i < points.length; i++) {
// let t = points.length === 1 ? 0 : i / (points.length - 1);
// let width = riverWidthMin + (1 - Math.abs(t * 2 - 1)) * (riverWidthMax - riverWidthMin);
// let dir;
// if (i === 0) {
// dir = points[1].clone().sub(points[0]);
// } else if (i === points.length - 1) {
// dir = points[i].clone().sub(points[i - 1]);
// } else {
// dir = points[i + 1].clone().sub(points[i - 1]);
// }
// dir.z = 0;
// dir.normalize();
// const normal = new THREE.Vector3(-dir.y, dir.x, 0);
// lefts.push(points[i].clone().add(normal.clone().multiplyScalar(width / 2)));
// rights.push(points[i].clone().add(normal.clone().multiplyScalar(-width / 2)));
// }
// (lineWaterJson.features || []).forEach((feature) => { // // 顶点、uv、透明度
// const geometry = feature.geometry; // const vertices = [];
// if (!geometry) return; // const uvs = [];
// let lines = []; // const alphas = [];
// if (geometry.type === "LineString") { // let totalLen = 0;
// lines = [geometry.coordinates]; // for (let i = 0; i < points.length; i++) {
// } else if (geometry.type === "MultiLineString") { // if (i > 0) totalLen += points[i].distanceTo(points[i - 1]);
// lines = geometry.coordinates; // let t = points.length === 1 ? 0 : i / (points.length - 1);
// } // let edgeAlpha = 0.7 + 0.3 * (1 - Math.abs(t * 2 - 1));
// lines.forEach((line) => { // // 左点
// // 生成带宽度的水带 // vertices.push(lefts[i]);
// const points = line.map(([lng, lat]) => { // uvs.push(totalLen * 2, 0); // UV缩放更密集
// const [x, y] = projection([lng, lat]); // alphas.push(edgeAlpha);
// return new THREE.Vector3(x, -y, this.depth + 0.26); // 保证高于地表 // // 右点
// }); // vertices.push(rights[i]);
// if (points.length < 2) return; // uvs.push(totalLen * 2, 1);
// // 用TubeGeometry生成带宽度的水带 // alphas.push(edgeAlpha);
// const curve = new THREE.CatmullRomCurve3(points); // }
// const tubeGeometry = new THREE.TubeGeometry(curve, Math.max(32, points.length * 2), 0.01, 16);
// const material = new THREE.MeshStandardMaterial({
// color: 0x4fc3ff,
// transparent: true,
// opacity: 0.35,
// side: DoubleSide,
// depthWrite: false,
// normalMap: normalMap,
// normalScale: new THREE.Vector2(2, 2),
// map: waterMap, // 使用颜色贴图
// metalness: 0.3,
// roughness: 0.7,
// });
// const mesh = new Mesh(tubeGeometry, material);
// mesh.renderOrder = 11;
// lineWaterGroup.add(mesh);
// });
// });
// return {
// lineWaterGroup,
// };
// }
// createLineWater() { // const indices = [];
// let mapJsonData = this.assets.instance.getResource("linewater"); // for (let i = 0; i < points.length - 1; i++) {
// let lineWaterJson; // const a = i * 2, b = i * 2 + 1, c = i * 2 + 2, d = i * 2 + 3;
// try { // indices.push(a, b, c, b, d, c);
// lineWaterJson = typeof mapJsonData === "string" ? JSON.parse(mapJsonData) : mapJsonData; // }
// } catch (e) {
// console.error("linewater 解析失败", e);
// return { lineWaterGroup: null };
// }
// const textureLoader = new THREE.TextureLoader(); // const geometry = new THREE.BufferGeometry();
// let base_url = import.meta.env.BASE_URL || "/"; // const posArr = new Float32Array(vertices.length * 3);
// const normalMap = textureLoader.load(base_url + "assets/json/waternormals.jpg"); // for (let i = 0; i < vertices.length; i++) {
// normalMap.wrapS = normalMap.wrapT = THREE.RepeatWrapping; // posArr[i * 3] = vertices[i].x;
// const waterMap = textureLoader.load(base_url + "assets/json/watercolor.png"); // posArr[i * 3 + 1] = vertices[i].y;
// waterMap.wrapS = waterMap.wrapT = THREE.RepeatWrapping; // posArr[i * 3 + 2] = vertices[i].z;
// }
// geometry.setAttribute('position', new THREE.BufferAttribute(posArr, 3));
// geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2));
// geometry.setAttribute('alpha', new THREE.BufferAttribute(new Float32Array(alphas), 1));
// geometry.setIndex(indices);
// // 流动速度更快 // // ShaderMaterial实现边缘羽化和流动水波
// this.time.on("tick", () => { // const material = new THREE.ShaderMaterial({
// normalMap.offset.x += 0.03; // uniforms: {
// normalMap.offset.y += 0.015; // map: { value: waterMap },
// waterMap.offset.x += 0.03; // normalMap: { value: normalMap },
// waterMap.offset.y += 0.015; // color: { value: new THREE.Color(0x4fc3ff) },
// }); // opacity: { value: 0.5 },
// },
// vertexShader: `
// attribute float alpha;
// varying vec2 vUv;
// varying float vAlpha;
// void main() {
// vUv = uv;
// vAlpha = alpha;
// gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
// }
// `,
// fragmentShader: `
// uniform sampler2D map;
// uniform sampler2D normalMap;
// uniform vec3 color;
// uniform float opacity;
// varying vec2 vUv;
// varying float vAlpha;
// void main() {
// vec4 texColor = texture2D(map, vUv);
// float alpha = texColor.a * opacity * vAlpha;
// vec3 finalColor = mix(color, texColor.rgb, 0.8); // 贴图主导
// if(alpha < 0.05) discard;
// gl_FragColor = vec4(finalColor, alpha);
// }
// `,
// transparent: true,
// side: DoubleSide,
// depthWrite: false,
// });
// const lineWaterGroup = new Group(); // const mesh = new Mesh(geometry, material);
// const projection = geoMercator().center(this.geoProjectionCenter).scale(this.geoProjectionScale).translate([0, 0]); // mesh.renderOrder = 11;
// lineWaterGroup.add(mesh);
// });
// });
// return {
// lineWaterGroup,
// };
// }
// 加载3d模型
showDroneAtLngLat(url, lng, lat, height, scale) {
// 经纬度转平面坐标
const [x, y] = this.geoProjection([lng, lat]);
// z 轴为地表高度加上无人机高度
const z = this.depth + height + 0.5;
// 创建 THREE.Vector3 位置
const position = new THREE.Vector3(x, z, -y);
// 调用已有的加载方法
this.loadDroneModel(url, position, scale);
}
/**
* 加载无人机 glb 模型并添加到场景
* @param {string} url glb模型路径
* @param {THREE.Vector3} position 模型放置位置
* @param {number} scale 模型缩放
*/
loadDroneModel(url, position = new THREE.Vector3(0, 0, 2), scale = 1) {
// 复用 Resource 工具或直接用 THREE.GLTFLoader
const loader =
this.terrainResource?.loaders?.GLTF || new THREE.GLTFLoader();
loader.load(
url,
(gltf) => {
const drone = gltf.scene;
drone.position.copy(position);
drone.scale.set(scale, scale, scale);
// 可根据需要调整旋转
// drone.rotation.set(0, Math.PI, 0);
// let propeller1 = drone.getObjectByName("phase8_Master25");
let propeller1 = drone.getObjectByName("prop_1_jnt34");
let propeller2 = drone.getObjectByName("prop_2_jnt35");
let propeller3 = drone.getObjectByName("prop_3_jnt36");
let propeller4 = drone.getObjectByName("prop_4_jnt37");
if (propeller1 && propeller2 && propeller3 && propeller4) {
// 每帧旋转扇叶
this.time.on("tick", () => {
propeller1.rotation.y += 0.5; // 控制转速
propeller2.rotation.y += 0.5;
propeller3.rotation.y += 0.5;
propeller4.rotation.y += 0.5;
});
} else {
console.warn("未找到扇叶节点,请确认节点名称");
}
this.scene.add(drone);
// (lineWaterJson.features || []).forEach((feature) => { // 添加交互事件
// const geometry = feature.geometry; this.interactionManager.add(drone);
// if (!geometry) return; drone.addEventListener("mousedown", (event) => {
// let lines = []; this.showDronePopup(drone);
// if (geometry.type === "LineString") { });
// lines = [geometry.coordinates]; this.moveDroneAlongPath(drone, [
// } else if (geometry.type === "MultiLineString") { [110.9801, 39.8002],
// lines = geometry.coordinates; [111.0806, 39.7005],
// } [111.1803, 39.6054],
// lines.forEach((line) => { [110.9008, 39.8056],
// const points = line.map(([lng, lat]) => { [111.1003, 39.7386],
// const [x, y] = projection([lng, lat]); ], 20);
// return new THREE.Vector3(x, -y, this.depth + 0.26); console.log("无人机模型加载完成", drone);
// }); },
// if (points.length < 2) return; (xhr) => {
// 加载进度
console.log(
`无人机模型加载进度: ${((xhr.loaded / xhr.total) * 100).toFixed(2)}%`
);
},
(error) => {
console.error("无人机模型加载失败:", error);
}
);
}
// 显示无人机弹窗
showDronePopup(drone) {
// 自动插入样式(只插入一次)
if (!document.getElementById("drone-popup-style")) {
const style = document.createElement("style");
style.id = "drone-popup-style";
style.innerHTML = `
.drone-popup {
z-index: 9999;
min-width: 140px;
max-width: 240px;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
font-size: 14px;
user-select: none;
transition: opacity 0.2s;
position: absolute;
background: rgba(0,0,0,0.8);
color: #fff;
padding: 10px 18px;
border-radius: 8px;
pointer-events: auto;
}
.drone-popup .close-btn {
position: absolute;
right: 8px;
top: 4px;
color: #fff;
cursor: pointer;
font-size: 16px;
opacity: 0.7;
}
.drone-popup .close-btn:hover {
opacity: 1;
}
.drone-popup video {
width: 100%;
margin-top: 8px;
border-radius: 4px;
background: #000;
display: block;
}
`;
document.head.appendChild(style);
}
// 如果已存在弹窗,先移除
if (this.dronePopup) {
document.body.removeChild(this.dronePopup);
this.time.off("tick", this._updateDronePopupPos);
this.dronePopup = null;
}
// // 宽度渐变:首尾变窄 // 创建弹窗
// const riverWidthMax = 0.08, riverWidthMin = 0.02; const popup = document.createElement("div");
// const lefts = [], rights = []; popup.className = "drone-popup";
// for (let i = 0; i < points.length; i++) { popup.innerHTML = `
// let t = points.length === 1 ? 0 : i / (points.length - 1); <span class="close-btn" title="关闭">&times;</span>
// let width = riverWidthMin + (1 - Math.abs(t * 2 - 1)) * (riverWidthMax - riverWidthMin); <div style="margin-top:4px;">
// let dir; <b>无人机信息</b><br>
// if (i === 0) { 位置:${drone.position.x.toFixed(2)}, ${drone.position.z.toFixed(2)}<br>
// dir = points[1].clone().sub(points[0]); <span style="font-size:12px;opacity:0.7;">(内容可自定义)</span>
// } else if (i === points.length - 1) { <video src="https://media.w3.org/2010/05/sintel/trailer.mp4" controls poster="" preload="metadata" custom-cache="false" ></video>
// dir = points[i].clone().sub(points[i - 1]); </div>
// } else { `;
// dir = points[i + 1].clone().sub(points[i - 1]); document.body.appendChild(popup);
// } this.dronePopup = popup;
// dir.z = 0; // 优化全屏性能卡顿
// dir.normalize(); document.addEventListener("fullscreenchange", () => {
// const normal = new THREE.Vector3(-dir.y, dir.x, 0); if (document.fullscreenElement) {
// lefts.push(points[i].clone().add(normal.clone().multiplyScalar(width / 2))); // 暂停 Three.js 动画
// rights.push(points[i].clone().add(normal.clone().multiplyScalar(-width / 2))); this.time.pause && this.time.pause();
// } } else {
// 恢复 Three.js 动画
this.time.resume && this.time.resume();
}
});
// // 顶点、uv、透明度 // 关闭按钮
// const vertices = []; popup.querySelector(".close-btn").onclick = () => {
// const uvs = []; if (this.dronePopup) {
// const alphas = []; document.body.removeChild(this.dronePopup);
// let totalLen = 0; this.time.off("tick", this._updateDronePopupPos);
// for (let i = 0; i < points.length; i++) { this.dronePopup = null;
// if (i > 0) totalLen += points[i].distanceTo(points[i - 1]); }
// let t = points.length === 1 ? 0 : i / (points.length - 1); };
// let edgeAlpha = 0.7 + 0.3 * (1 - Math.abs(t * 2 - 1));
// // 左点
// vertices.push(lefts[i]);
// uvs.push(totalLen * 2, 0); // UV缩放更密集
// alphas.push(edgeAlpha);
// // 右点
// vertices.push(rights[i]);
// uvs.push(totalLen * 2, 1);
// alphas.push(edgeAlpha);
// }
// const indices = []; // 跟随无人机位置
// for (let i = 0; i < points.length - 1; i++) { this._updateDronePopupPos = () => {
// const a = i * 2, b = i * 2 + 1, c = i * 2 + 2, d = i * 2 + 3; // 获取无人机世界坐标
// indices.push(a, b, c, b, d, c); const worldPos = new THREE.Vector3();
// } drone.getWorldPosition(worldPos);
// 转换为屏幕坐标
worldPos.project(this.camera.instance);
let x = (worldPos.x * 0.5 + 0.5) * window.innerWidth;
let y = (1 - (worldPos.y * 0.5 + 0.5)) * window.innerHeight;
// const geometry = new THREE.BufferGeometry(); // 防止弹窗超出屏幕
// const posArr = new Float32Array(vertices.length * 3); const rect = popup.getBoundingClientRect();
// for (let i = 0; i < vertices.length; i++) { x = Math.min(
// posArr[i * 3] = vertices[i].x; Math.max(0, x - rect.width / 2),
// posArr[i * 3 + 1] = vertices[i].y; window.innerWidth - rect.width
// posArr[i * 3 + 2] = vertices[i].z; );
// } y = Math.min(
// geometry.setAttribute('position', new THREE.BufferAttribute(posArr, 3)); Math.max(0, y - rect.height - 20),
// geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2)); window.innerHeight - rect.height
// geometry.setAttribute('alpha', new THREE.BufferAttribute(new Float32Array(alphas), 1)); );
// geometry.setIndex(indices);
// // ShaderMaterial实现边缘羽化和流动水波 popup.style.left = `${x}px`;
// const material = new THREE.ShaderMaterial({ popup.style.top = `${y}px`;
// uniforms: { };
// map: { value: waterMap }, this.time.on("tick", this._updateDronePopupPos);
// normalMap: { value: normalMap }, // 初始化一次
// color: { value: new THREE.Color(0x4fc3ff) }, this._updateDronePopupPos();
// opacity: { value: 0.5 }, }
// }, /**
// vertexShader: ` * 让无人机沿指定经纬度路径平滑移动
// attribute float alpha; * @param {THREE.Object3D} drone 无人机模型对象
// varying vec2 vUv; * @param {Array} lngLatArr 经纬度数组
// varying float vAlpha; * @param {number} duration 动画时长(秒)
// void main() { */
// vUv = uv; moveDroneAlongPath(drone, lngLatArr, duration = 10) {
// vAlpha = alpha; // 1. 经纬度转three世界坐标
// gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); const z = this.depth + 1; // 高度可调整
// } const points = lngLatArr.map(([lng, lat]) => {
// `, const [x, y] = this.geoProjection([lng, lat]);
// fragmentShader: ` return new THREE.Vector3(x, z, y);
// uniform sampler2D map; });
// uniform sampler2D normalMap; // 2. 创建平滑曲线
// uniform vec3 color; const curve = new THREE.CatmullRomCurve3(points, false, "catmullrom", 0.5);
// uniform float opacity;
// varying vec2 vUv;
// varying float vAlpha;
// void main() {
// vec4 texColor = texture2D(map, vUv);
// float alpha = texColor.a * opacity * vAlpha;
// vec3 finalColor = mix(color, texColor.rgb, 0.8); // 贴图主导
// if(alpha < 0.05) discard;
// gl_FragColor = vec4(finalColor, alpha);
// }
// `,
// transparent: true,
// side: DoubleSide,
// depthWrite: false,
// });
// const mesh = new Mesh(geometry, material); // 3. 动画
// mesh.renderOrder = 11; let startTime = null;
// lineWaterGroup.add(mesh); const animate = (now) => {
// }); if (!startTime) startTime = now;
// }); const elapsed = (now - startTime) / 1000;
// return { let t = elapsed / duration;
// lineWaterGroup, if (t > 1) t = 1;
// }; // 沿曲线取点
// } const pos = curve.getPoint(t);
drone.position.copy(pos);
// 可选:让无人机朝向运动方向
if (t < 1) {
const nextPos = curve.getPoint(Math.min(t + 0.01, 1));
drone.lookAt(nextPos);
}
if (t < 1) {
drone._moveReq = requestAnimationFrame(animate);
}
};
// 停止之前的动画
if (drone._moveReq) cancelAnimationFrame(drone._moveReq);
drone._moveReq = requestAnimationFrame(animate);
}
// 跳转到指定group // 跳转到指定group
zoomToFocusMapGroup(group) { zoomToFocusMapGroup(group) {
// 创建一个 Box3 来计算 focusMapGroup 的边界盒 // 创建一个 Box3 来计算 focusMapGroup 的边界盒
...@@ -1966,7 +2255,9 @@ createLineWater() { ...@@ -1966,7 +2255,9 @@ createLineWater() {
distance *= 1.2; distance *= 1.2;
// 设置摄像机位置(沿着 Z 轴方向) // 设置摄像机位置(沿着 Z 轴方向)
const cameraPosition = center.clone().add(new THREE.Vector3(0, 0, distance)); const cameraPosition = center
.clone()
.add(new THREE.Vector3(0, 0, distance));
this.camera.instance.position.copy(cameraPosition); this.camera.instance.position.copy(cameraPosition);
// 让摄像机看向 focusMapGroup 的中心 // 让摄像机看向 focusMapGroup 的中心
...@@ -1976,14 +2267,17 @@ createLineWater() { ...@@ -1976,14 +2267,17 @@ createLineWater() {
this.camera.instance.updateProjectionMatrix(); this.camera.instance.updateProjectionMatrix();
} }
geoProjection(args) { geoProjection(args) {
return geoMercator().center(this.geoProjectionCenter).scale(this.geoProjectionScale).translate([0, 0])(args) return geoMercator()
.center(this.geoProjectionCenter)
.scale(this.geoProjectionScale)
.translate([0, 0])(args);
} }
update() { update() {
super.update() super.update();
this.interactionManager && this.interactionManager.update() this.interactionManager && this.interactionManager.update();
} }
destroy() { destroy() {
super.destroy() super.destroy();
this.label3d && this.label3d.destroy() this.label3d && this.label3d.destroy();
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论