3D地图提交一下
This commit is contained in:
@@ -3,8 +3,44 @@ import axios from 'axios'
|
||||
import Vue from 'vue'
|
||||
|
||||
window.axios = axios
|
||||
const KENGEE_CDN_BASE = "http://10.0.97.209/presource/datascreen/"
|
||||
const libs = [`${KENGEE_CDN_BASE}/js/pinyin.min.js`, `${KENGEE_CDN_BASE}/js/dayjs.min.js`]
|
||||
const KENGEE_CDN_BASE = "http://10.0.97.209/presource/datascreen"
|
||||
const libs = [
|
||||
`${KENGEE_CDN_BASE}/js/pinyin.min.js`,
|
||||
`${KENGEE_CDN_BASE}/js/dayjs.min.js`,
|
||||
`${KENGEE_CDN_BASE}/js/Tween.js`,
|
||||
`${KENGEE_CDN_BASE}/js/three/three.js`,
|
||||
`${KENGEE_CDN_BASE}/js/three/js/controls/OrbitControls.js`,
|
||||
`${KENGEE_CDN_BASE}/js/d3-geo.min.js`,
|
||||
]
|
||||
window.$loadScript = (type = 'js', url, dom = "body") => {
|
||||
let flag = false;
|
||||
return new Promise((resolve) => {
|
||||
const head = dom == 'head' ? document.getElementsByTagName('head')[0] : document.body;
|
||||
for (let i = 0; i < head.children.length; i++) {
|
||||
let ele = head.children[i]
|
||||
if ((ele.src || '').indexOf(url) !== -1) {
|
||||
flag = true;
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
if (flag) return;
|
||||
let script;
|
||||
if (type === 'js') {
|
||||
script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = url;
|
||||
} else if (type === 'css') {
|
||||
script = document.createElement('link');
|
||||
script.rel = 'stylesheet';
|
||||
script.type = 'text/css';
|
||||
script.href = url;
|
||||
}
|
||||
head.appendChild(script);
|
||||
script.onload = function () {
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
};
|
||||
window.$glob = {}
|
||||
window.$dicts = dicts
|
||||
window.$waitFor = (target, t = 500) => new Promise(resolve => {
|
||||
@@ -50,10 +86,5 @@ Vue.component("tableColumn", {
|
||||
|
||||
export default Promise.all([
|
||||
import("./fetch"),
|
||||
...libs.map(url => new Promise(resolve => {
|
||||
const script = document.createElement("script")
|
||||
script.src = url
|
||||
document.head.appendChild(script)
|
||||
script.onload = () => resolve()
|
||||
})),
|
||||
...libs.map(url => $loadScript('js', url)),
|
||||
])
|
||||
|
||||
@@ -1,196 +1,552 @@
|
||||
<script>
|
||||
import * as THREE from "https://cdn.bootcdn.net/ajax/libs/three.js/0.165.0/three.module.js"
|
||||
const {TWEEN, THREE, d3, axios, OrbitControls} = window
|
||||
class GeoMap {
|
||||
constructor() {
|
||||
this.cameraPosition = {x: 100, y: 0, z: 100}; // 相机位置
|
||||
this.scene = null; // 场景
|
||||
this.camera = null; // 相机
|
||||
this.renderer = null; // 渲染器
|
||||
this.controls = null; // 控制器
|
||||
this.mapGroup = []; // 组
|
||||
this.meshList = []; // 接受鼠标事件对象
|
||||
this.selectObject = null; // 当前选中对象
|
||||
this.loopIndex = 0; // 循环标记
|
||||
this.cameraPath = null; // 相机运动轨迹
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 初始化
|
||||
* */
|
||||
|
||||
init() {
|
||||
this.setScene();
|
||||
this.setCamera();
|
||||
this.setLight();
|
||||
this.setRenderer();
|
||||
this.setControl();
|
||||
this.setAxes();
|
||||
this.makeGround();
|
||||
this.getMap('https://geo.datav.aliyun.com/areas_v3/bound/330000_full.json', '开封');
|
||||
this.animat();
|
||||
this.bindMouseEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 动画循环
|
||||
* */
|
||||
|
||||
animat() {
|
||||
requestAnimationFrame(this.animat.bind(this));
|
||||
this.lightWave();
|
||||
// this.moveCamera();
|
||||
TWEEN.update();
|
||||
this.controls.update();
|
||||
this.renderer.render(this.scene, this.camera);
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 获取地图
|
||||
* */
|
||||
|
||||
getMap(url, type) {
|
||||
const that = this;
|
||||
axios.get(url).then(function (res) {
|
||||
if (res.status === 200) {
|
||||
const data = res.data;
|
||||
that.setMapData(data, type)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 添加基础灯光
|
||||
* */
|
||||
|
||||
setLight() {
|
||||
const pointLight = new THREE.PointLight(0xffffff, 1, 0);
|
||||
pointLight.position.set(0, 0, 5);
|
||||
this.scene.add(pointLight);
|
||||
|
||||
const sphereSize = 1;
|
||||
const pointLightHelper = new THREE.PointLightHelper(pointLight, sphereSize);
|
||||
this.scene.add(pointLightHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 绘制地图
|
||||
* @params geojson
|
||||
* */
|
||||
|
||||
setMapData(data, type) {
|
||||
const that = this;
|
||||
let vector3json = [];
|
||||
data.features.forEach(function (features, featuresIndex) {
|
||||
const areaItems = features.geometry.coordinates;
|
||||
features.properties.cp = that.lnglatToVector3(features.properties.cp);
|
||||
vector3json[featuresIndex] = {
|
||||
data: features.properties,
|
||||
mercator: []
|
||||
};
|
||||
areaItems.forEach(function (item, areaIndex) {
|
||||
vector3json[featuresIndex].mercator[areaIndex] = [];
|
||||
item.forEach(function (cp) {
|
||||
const lnglat = that.lnglatToVector3(cp);
|
||||
const vector3 = new THREE.Vector3(lnglat[0], lnglat[1], lnglat[2]).multiplyScalar(1.2);
|
||||
vector3json[featuresIndex].mercator[areaIndex].push(vector3)
|
||||
})
|
||||
})
|
||||
});
|
||||
if (type === 'sichuan') {
|
||||
this.drawMap(vector3json)
|
||||
} else if (type === 'china') {
|
||||
this.drawChinaMap(vector3json)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 绘制图形
|
||||
* @param data
|
||||
* */
|
||||
drawMap(data) {
|
||||
let that = this;
|
||||
this.mapGroup = new THREE.Group();
|
||||
this.mapGroup.position.y = 0;
|
||||
this.scene.add(that.mapGroup);
|
||||
const extrudeSettings = {
|
||||
depth: 0.8,
|
||||
steps: 1,
|
||||
bevelSegments: 0,
|
||||
curveSegments: 1,
|
||||
bevelEnabled: false,
|
||||
};
|
||||
const blockMaterial = new THREE.MeshBasicMaterial({
|
||||
color: '#3700b1',
|
||||
opacity: 0.7,
|
||||
transparent: true,
|
||||
wireframe: false
|
||||
});
|
||||
const blockSideMaterial = new THREE.MeshBasicMaterial({
|
||||
color: '#5923bc',
|
||||
opacity: 0.7,
|
||||
transparent: true,
|
||||
wireframe: false
|
||||
});
|
||||
const lineMaterial = new THREE.LineBasicMaterial({
|
||||
color: '#9800ff'
|
||||
});
|
||||
data.forEach(function (areaData) {
|
||||
let areaGroup = new THREE.Group();
|
||||
areaGroup.name = 'area';
|
||||
areaGroup._groupType = 'areaBlock';
|
||||
areaData.mercator.forEach(function (areaItem) {
|
||||
// Draw area block
|
||||
let shape = new THREE.Shape(areaItem);
|
||||
let geometry = new THREE.ExtrudeBufferGeometry(shape, extrudeSettings);
|
||||
let mesh = new THREE.Mesh(geometry, [blockMaterial, blockSideMaterial]);
|
||||
areaGroup.add(mesh);
|
||||
// Draw Line
|
||||
let lineGeometry = new THREE.Geometry();
|
||||
lineGeometry.vertices = areaItem;
|
||||
let lineMesh = new THREE.Line(lineGeometry, lineMaterial);
|
||||
let lineMeshCp = lineMesh.clone();
|
||||
lineMeshCp.position.z = 0.8;
|
||||
areaGroup.add(lineMesh);
|
||||
areaGroup.add(lineMeshCp);
|
||||
// add mesh to meshList for mouseEvent
|
||||
that.meshList.push(mesh);
|
||||
});
|
||||
areaGroup.add(that.lightGroup(areaData));
|
||||
areaGroup.add(that.tipsSprite(areaData));
|
||||
that.mapGroup.add(areaGroup);
|
||||
});
|
||||
that.scene.add(that.mapGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 绘制图形作为背景
|
||||
* @param data
|
||||
* */
|
||||
drawChinaMap(data) {
|
||||
let that = this;
|
||||
let mapGroup = new THREE.Group();
|
||||
mapGroup.position.y = 0;
|
||||
this.scene.add(mapGroup);
|
||||
const lineMaterial = new THREE.LineDashedMaterial({
|
||||
color: '#656565',
|
||||
dashSize: 0.1,
|
||||
gapSize: 0.2
|
||||
});
|
||||
let fakeLightMaterial = new THREE.MeshBasicMaterial({
|
||||
color: 0xffffff,
|
||||
flatShading: true,
|
||||
vertexColors: THREE.VertexColors,
|
||||
side: THREE.DoubleSide,
|
||||
transparent: true,
|
||||
opacity: 0.5,
|
||||
depthTest: false,
|
||||
wireframe: false,
|
||||
});
|
||||
data.forEach(function (areaData) {
|
||||
if (areaData.data.id === '51') {
|
||||
areaData.mercator.forEach(function (areaItem) {
|
||||
let geometry = new THREE.BufferGeometry();
|
||||
let verticesArr = [];
|
||||
for (let i = 0; i < areaItem.length - 1; i++) {
|
||||
verticesArr.push(areaItem[i].x, areaItem[i].y, areaItem[i].z);
|
||||
verticesArr.push(areaItem[i + 1].x, areaItem[i + 1].y, areaItem[i + 1].z + 5);
|
||||
verticesArr.push(areaItem[i].x, areaItem[i].y, areaItem[i].z + 5);
|
||||
|
||||
verticesArr.push(areaItem[i].x, areaItem[i].y, areaItem[i].z);
|
||||
verticesArr.push(areaItem[i + 1].x, areaItem[i + 1].y, areaItem[i + 1].z);
|
||||
verticesArr.push(areaItem[i + 1].x, areaItem[i + 1].y, areaItem[i + 1].z + 5);
|
||||
}
|
||||
geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(verticesArr), 3));
|
||||
let count = geometry.attributes.position.count;
|
||||
geometry.addAttribute('color', new THREE.BufferAttribute(new Float32Array(count * 3), 3));
|
||||
let color = new THREE.Color();
|
||||
let positions = geometry.attributes.position;
|
||||
let colors = geometry.attributes.color;
|
||||
for (let i = 0; i < count; i++) {
|
||||
let a = positions.getZ(i) ? 0 : 1;
|
||||
color.setHSL((268 * a) / 360, 1.0 * a, a ? 0.5 : 0.13);
|
||||
colors.setXYZ(i, color.r, color.g, color.b);
|
||||
}
|
||||
let mesh = new THREE.Mesh(geometry, fakeLightMaterial);
|
||||
mapGroup.add(mesh);
|
||||
});
|
||||
|
||||
} else {
|
||||
areaData.mercator.forEach(function (areaItem) {
|
||||
// Draw Line
|
||||
let lineGeometry = new THREE.Geometry();
|
||||
lineGeometry.vertices = areaItem;
|
||||
let lineMesh = new THREE.Line(lineGeometry, lineMaterial);
|
||||
lineMesh.computeLineDistances();
|
||||
mapGroup.add(lineMesh);
|
||||
});
|
||||
}
|
||||
});
|
||||
that.scene.add(mapGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 移动相机
|
||||
* */
|
||||
moveCamera() {
|
||||
// 第一次绘制相机路径
|
||||
if (this.cameraPath === null) {
|
||||
this.cameraPath = new THREE.Path();
|
||||
this.cameraPath.moveTo(150, 0);
|
||||
this.cameraPath.lineTo(70, 0);
|
||||
let geometry = new THREE.BufferGeometry().setFromPoints(this.cameraPath.getPoints());
|
||||
let material = new THREE.LineBasicMaterial({color: 0xff0000});
|
||||
let line = new THREE.Line(geometry, material);
|
||||
line.position.z = 100;
|
||||
this.scene.add(line);
|
||||
this.progress = 0;
|
||||
} else {
|
||||
if (this.progress < 1) {
|
||||
this.progress += 0.01; // 增量 也就是说将该线端,按照1/500的比例进行分割。也就是说有500个坐标点
|
||||
let point = this.cameraPath.getPointAt(this.progress); // 从路径中拿取坐标点点
|
||||
if (point) {
|
||||
this.camera.position.set(point.x, point.y, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 坐标转换
|
||||
* @param lnglat [x,y]
|
||||
* */
|
||||
lnglatToVector3(lnglat) {
|
||||
if (!this.projection) {
|
||||
this.projection = d3.geoMercator().center([104.072259, 30.663403]).scale(100).translate([0, 0]);
|
||||
}
|
||||
const [x, y] = this.projection([lnglat[0], lnglat[1]])
|
||||
const z = 0;
|
||||
return [y, x, z]
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 创建场景
|
||||
* */
|
||||
setScene() {
|
||||
this.scene = new THREE.Scene();
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 创建相机
|
||||
* */
|
||||
setCamera() {
|
||||
this.camera = new THREE.PerspectiveCamera(10, window.innerWidth / window.innerHeight, 1, 2000);
|
||||
this.camera.up.x = 0;
|
||||
this.camera.up.y = 0;
|
||||
this.camera.up.z = 1;
|
||||
this.camera.lookAt(0, 0, 0);
|
||||
this.scene.add(this.camera);
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 创建渲染器
|
||||
* */
|
||||
setRenderer() {
|
||||
this.renderer = new THREE.WebGLRenderer({antialias: true});
|
||||
this.renderer.setPixelRatio(window.devicePixelRatio * 1);
|
||||
this.renderer.sortObjects = true; // 渲染顺序
|
||||
this.renderer.setClearColor('#212121');
|
||||
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
this.$el.appendChild(this.renderer.domElement);
|
||||
|
||||
function onWindowResize() {
|
||||
this.camera.aspect = window.innerWidth / window.innerHeight;
|
||||
this.camera.updateProjectionMatrix();
|
||||
this.renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', onWindowResize.bind(this), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 创建控制器
|
||||
* */
|
||||
setControl() {
|
||||
this.controls = new THREE.OrbitControls(this.camera);
|
||||
this.camera.position.set(this.cameraPosition.x, this.cameraPosition.y, this.cameraPosition.z);
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 创建一个xyz坐标轴
|
||||
* */
|
||||
setAxes() {
|
||||
const axes = new THREE.AxesHelper(100);
|
||||
this.scene.add(axes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 鼠标 hover 事件
|
||||
* */
|
||||
|
||||
bindMouseEvent() {
|
||||
let that = this;
|
||||
|
||||
function onMouseMove(event) {
|
||||
const x = (event.clientX / window.innerWidth) * 2 - 1; //标准设备横坐标
|
||||
const y = -(event.clientY / window.innerHeight) * 2 + 1; //标准设备纵坐标
|
||||
const standardVector = new THREE.Vector3(x, y, 0.5); //标准设备坐标
|
||||
//标准设备坐标转世界坐标
|
||||
const worldVector = standardVector.unproject(that.camera);
|
||||
//射线投射方向单位向量(worldVector坐标减相机位置坐标)
|
||||
const ray = worldVector.sub(that.camera.position).normalize();
|
||||
//创建射线投射器对象
|
||||
let raycaster = new THREE.Raycaster(that.camera.position, ray);
|
||||
//返回射线选中的对象
|
||||
let intersects = raycaster.intersectObjects(that.meshList);
|
||||
if (intersects.length) {
|
||||
if (intersects[0].object.parent && intersects[0].object.parent._groupType === 'areaBlock') {
|
||||
if (that.selectObject !== intersects[0].object.parent) {
|
||||
if (that.selectObject) {
|
||||
transiform(that.selectObject.position, {
|
||||
x: that.selectObject.position.x,
|
||||
y: that.selectObject.position.y,
|
||||
z: 0
|
||||
}, 100);
|
||||
transiform(intersects[0].object.parent.position, {
|
||||
x: intersects[0].object.parent.position.x,
|
||||
y: intersects[0].object.parent.position.y,
|
||||
z: 0.8
|
||||
}, 100);
|
||||
that.selectObject = intersects[0].object.parent;
|
||||
} else {
|
||||
transiform(intersects[0].object.parent.position, {
|
||||
x: intersects[0].object.parent.position.x,
|
||||
y: intersects[0].object.parent.position.y,
|
||||
z: 0.8
|
||||
}, 100);
|
||||
that.selectObject = intersects[0].object.parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function transiform(o, n, t) {
|
||||
let e = new TWEEN.Tween(o)
|
||||
.to(n, t)
|
||||
.start();
|
||||
}
|
||||
|
||||
window.addEventListener('mousemove', onMouseMove, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 创建地面函数
|
||||
* */
|
||||
makeGround() {
|
||||
const maps = new THREE.TextureLoader().load('/images/bgf.png');
|
||||
maps.wrapS = maps.wrapT = THREE.RepeatWrapping;
|
||||
maps.repeat.set(14, 14); // 纹理 y,x方向重铺
|
||||
maps.needsUpdate = false; // 纹理更新
|
||||
let material = new THREE.MeshBasicMaterial({
|
||||
// map: maps,
|
||||
opacity: 1,
|
||||
transparent: true,
|
||||
color: '#212121'
|
||||
});
|
||||
const geometry = new THREE.PlaneGeometry(100, 100, 1, 1);
|
||||
let ground = new THREE.Mesh(geometry, material);
|
||||
ground.position.x = 0;
|
||||
ground.position.y = 0;
|
||||
ground.position.z = -1;
|
||||
this.scene.add(ground);
|
||||
ground.receiveShadow = true;
|
||||
ground.castShadow = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 光柱
|
||||
* */
|
||||
|
||||
lightGroup(areaData) {
|
||||
/*光柱*/
|
||||
const lightMapTexture = new THREE.TextureLoader().load('/images/light.png');
|
||||
lightMapTexture.repeat.set(1, 1); // 纹理 y,x方向重铺
|
||||
lightMapTexture.needsUpdate = false; // 纹理更新
|
||||
let lightTipGroup = new THREE.Group();
|
||||
lightTipGroup.name = 'lightTipGroup'
|
||||
let lightGeometry = new THREE.PlaneBufferGeometry(2, 0.5, 1);
|
||||
let lightMaterial = new THREE.MeshBasicMaterial({
|
||||
map: lightMapTexture,
|
||||
side: THREE.DoubleSide,
|
||||
blending: THREE.AdditiveBlending,
|
||||
depthTest: false,
|
||||
transparent: true,
|
||||
opacity: 0.5
|
||||
});
|
||||
let lightPlane = new THREE.Mesh(lightGeometry, lightMaterial);
|
||||
lightPlane.rotation.y = Math.PI / 2;
|
||||
lightPlane.position.x = 0;
|
||||
lightPlane.position.y = 0;
|
||||
lightPlane.position.z = 0;
|
||||
lightTipGroup.add(lightPlane);
|
||||
|
||||
let lightMeshCp = lightPlane.clone();
|
||||
lightMeshCp.rotation.x = Math.PI / 2;
|
||||
lightMeshCp.rotation.y = 0;
|
||||
lightMeshCp.rotation.z = -Math.PI / 2;
|
||||
lightTipGroup.add(lightMeshCp);
|
||||
|
||||
let circleGeometry = new THREE.CircleBufferGeometry(0.2, 20);
|
||||
let circleMaterial = new THREE.MeshBasicMaterial({
|
||||
side: THREE.DoubleSide,
|
||||
blending: THREE.AdditiveBlending,
|
||||
color: '#ff007e',
|
||||
depthTest: false,
|
||||
transparent: true,
|
||||
opacity: 1
|
||||
});
|
||||
let circleMesh = new THREE.Mesh(circleGeometry, circleMaterial);
|
||||
circleMesh.position.z = -0.99;
|
||||
|
||||
circleMesh.renderOrder = 1;
|
||||
lightTipGroup.add(circleMesh);
|
||||
|
||||
let circleCpGeometry = new THREE.CircleBufferGeometry(0.2, 20);
|
||||
let circleCpMaterial = new THREE.MeshBasicMaterial({
|
||||
side: THREE.DoubleSide,
|
||||
blending: THREE.AdditiveBlending,
|
||||
color: 0xffffff,
|
||||
depthTest: false,
|
||||
transparent: true,
|
||||
opacity: 1
|
||||
});
|
||||
let circleMeshCp = new THREE.Mesh(circleCpGeometry, circleCpMaterial);
|
||||
circleMeshCp.name = 'circleMesh';
|
||||
circleMeshCp.position.z = -0.995;
|
||||
lightTipGroup.add(circleMeshCp);
|
||||
|
||||
lightTipGroup.position.x = areaData.data.cp[0];
|
||||
lightTipGroup.position.y = areaData.data.cp[1];
|
||||
lightTipGroup.position.z = 1.5;
|
||||
lightTipGroup.rotation.z = Math.PI / 4;
|
||||
lightTipGroup.renderOrder = 2;
|
||||
|
||||
return lightTipGroup
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 地区名称 采用sprite
|
||||
* */
|
||||
|
||||
tipsSprite(areaData) {
|
||||
let canvas = document.createElement("canvas");
|
||||
canvas.width = 500;
|
||||
canvas.height = 60;
|
||||
document.body.appendChild(canvas);
|
||||
|
||||
let ctx = canvas.getContext("2d");
|
||||
|
||||
ctx.fillStyle = "#ffffff";
|
||||
ctx.font = "50px Arial";
|
||||
ctx.textAlign = "center";
|
||||
ctx.fillText(areaData.data.name, 250, 40);
|
||||
|
||||
let texture = new THREE.CanvasTexture(canvas);
|
||||
texture.needsUpdate = true;
|
||||
let SpriteMaterial = new THREE.SpriteMaterial({
|
||||
map: texture,
|
||||
depthTest: false,
|
||||
});
|
||||
|
||||
let textSprite = new THREE.Sprite(SpriteMaterial);
|
||||
textSprite.position.set(areaData.data.cp[0], areaData.data.cp[1], 1);
|
||||
textSprite.scale.set(4, 0.5, 1);
|
||||
textSprite.renderOrder = 3;
|
||||
|
||||
return textSprite
|
||||
}
|
||||
|
||||
/**
|
||||
* @desc 光柱波纹动画
|
||||
* */
|
||||
|
||||
lightWave() {
|
||||
if (this.mapGroup.children) {
|
||||
let that = this;
|
||||
if (this.loopIndex >= 1) {
|
||||
this.loopIndex = 0;
|
||||
} else {
|
||||
this.loopIndex += 0.02;
|
||||
}
|
||||
this.mapGroup.children.forEach(function (item) {
|
||||
if (item.name === 'area') {
|
||||
item.children.forEach(function (g) {
|
||||
if (g.name === 'lightTipGroup') {
|
||||
g.children.forEach(function (c) {
|
||||
if (c.name === 'circleMesh') {
|
||||
c.scale = {
|
||||
x: 4 * Math.asin(that.loopIndex) + 1,
|
||||
y: 4 * Math.asin(that.loopIndex) + 1,
|
||||
z: 4 * Math.asin(that.loopIndex) + 1
|
||||
};
|
||||
c.material.opacity = 0.3 * Math.acos(that.loopIndex) - 0.1
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "AppThreeMap",
|
||||
label: "3D地图",
|
||||
mounted() {
|
||||
const scene = new THREE.Scene()
|
||||
const ambientLight = new THREE.AmbientLight(0xd4e7fd, 4);
|
||||
scene.add(ambientLight);
|
||||
const directionalLight = new THREE.DirectionalLight(0xe8eaeb, 0.2);
|
||||
directionalLight.position.set(0, 10, 5);
|
||||
const directionalLight2 = directionalLight.clone();
|
||||
directionalLight2.position.set(0, 10, -5);
|
||||
const directionalLight3 = directionalLight.clone();
|
||||
directionalLight3.position.set(5, 10, 0);
|
||||
const directionalLight4 = directionalLight.clone();
|
||||
directionalLight4.position.set(-5, 10, 0);
|
||||
scene.add(directionalLight);
|
||||
scene.add(directionalLight2);
|
||||
scene.add(directionalLight3);
|
||||
scene.add(directionalLight4);
|
||||
|
||||
const camera = new THREE.PerspectiveCamera(
|
||||
75,
|
||||
window.innerWidth / window.innerHeight,
|
||||
0.1,
|
||||
1000
|
||||
);
|
||||
camera.position.y = 8;
|
||||
camera.position.z = 8;
|
||||
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({alpha: true});
|
||||
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
this.$el.appendChild(renderer.domElement);
|
||||
// const controls = new OrbitControls(camera, renderer.domElement);
|
||||
// controls.update();
|
||||
const animate = () => {
|
||||
renderer.render(scene, camera);
|
||||
requestAnimationFrame(animate);
|
||||
// controls.update();
|
||||
};
|
||||
window.addEventListener("resize", () => {
|
||||
camera.aspect = window.innerWidth / window.innerHeight;
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
});
|
||||
|
||||
const url = "https://geo.datav.aliyun.com/areas_v3/bound/330000_full.json";
|
||||
fetch(url).then((res) => res.json())
|
||||
.then((data) => {
|
||||
const map = createMap(data);
|
||||
scene.add(map);
|
||||
|
||||
let intersect = null;
|
||||
window.addEventListener("pointerdown", (event) => {
|
||||
const mouse = new THREE.Vector2();
|
||||
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
||||
const raycaster = new THREE.Raycaster();
|
||||
raycaster.setFromCamera(mouse, camera);
|
||||
const intersects = raycaster
|
||||
.intersectObjects(map.children)
|
||||
.filter((item) => item.object.type !== "Line");
|
||||
if (intersects.length > 0) {
|
||||
if (intersects[0].object.type === "Mesh") {
|
||||
if (intersect) isAplha(intersect, 1);
|
||||
intersect = intersects[0].object.parent;
|
||||
isAplha(intersect, 0.4);
|
||||
}
|
||||
if (intersects[0].object.type === "Sprite") {
|
||||
console.log(intersects[0].object);
|
||||
}
|
||||
} else {
|
||||
if (intersect) isAplha(intersect, 1);
|
||||
}
|
||||
|
||||
function isAplha(intersect, opacity) {
|
||||
intersect.children.forEach((item) => {
|
||||
if (item.type === "Mesh") {
|
||||
item.material.opacity = opacity;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const createMap = (data) => {
|
||||
const map = new THREE.Object3D();
|
||||
const center = data.features[0].properties.centroid;
|
||||
data.features.forEach((feature) => {
|
||||
const unit = new THREE.Object3D();
|
||||
const {centroid, center, name} = feature.properties;
|
||||
const {coordinates, type} = feature.geometry;
|
||||
const point = centroid || center || [0, 0];
|
||||
|
||||
const color = new THREE.Color(`hsl(
|
||||
${233},
|
||||
${Math.random() * 30 + 55}%,
|
||||
${Math.random() * 30 + 55}%)`).getHex();
|
||||
const depth = Math.random() * 0.3 + 0.3;
|
||||
|
||||
// const label = createLabel(name, point, depth);
|
||||
// const icon = createIcon(center, depth);
|
||||
|
||||
coordinates.forEach((coordinate) => {
|
||||
if (type === "MultiPolygon") coordinate.forEach((item) => fn(item));
|
||||
if (type === "Polygon") fn(coordinate);
|
||||
|
||||
function fn(coordinate) {
|
||||
unit.name = name;
|
||||
const mesh = createMesh(coordinate, color, depth);
|
||||
const line = createLine(coordinate, depth);
|
||||
unit.add(mesh, ...line);
|
||||
}
|
||||
});
|
||||
map.add(unit);
|
||||
setCenter(map);
|
||||
});
|
||||
return map;
|
||||
};
|
||||
const createMesh = (data, color, depth) => {
|
||||
const shape = new THREE.Shape();
|
||||
|
||||
const extrudeSettings = {
|
||||
depth: depth,
|
||||
bevelEnabled: false,
|
||||
};
|
||||
const materialSettings = {
|
||||
color: color,
|
||||
emissive: 0x000000,
|
||||
roughness: 0.45,
|
||||
metalness: 0.8,
|
||||
transparent: true,
|
||||
side: THREE.DoubleSide,
|
||||
};
|
||||
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
|
||||
const material = new THREE.MeshStandardMaterial(materialSettings);
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
|
||||
return mesh;
|
||||
};
|
||||
const createLine = (data, depth) => {
|
||||
const points = [];
|
||||
const lineGeometry = new THREE.BufferGeometry().setFromPoints(points);
|
||||
const uplineMaterial = new THREE.LineBasicMaterial({color: 0xffffff});
|
||||
const downlineMaterial = new THREE.LineBasicMaterial({color: 0xffffff});
|
||||
|
||||
const upLine = new THREE.Line(lineGeometry, uplineMaterial);
|
||||
const downLine = new THREE.Line(lineGeometry, downlineMaterial);
|
||||
downLine.position.z = -0.0001;
|
||||
upLine.position.z = depth + 0.0001;
|
||||
return [upLine, downLine];
|
||||
};
|
||||
const createLabel = (name, point, depth) => {
|
||||
const div = document.createElement("div");
|
||||
div.style.color = "#fff";
|
||||
div.style.fontSize = "12px";
|
||||
div.style.textShadow = "1px 1px 2px #047cd6";
|
||||
div.textContent = name;
|
||||
const label = new CSS2DObject(div);
|
||||
label.scale.set(0.01, 0.01, 0.01);
|
||||
// const [x, y] = offsetXY(point);
|
||||
// label.position.set(x, -y, depth);
|
||||
return label;
|
||||
};
|
||||
const createIcon = (point, depth) => {
|
||||
const url = new URL("../assets/icon.png", import.meta.url).href;
|
||||
const map = new THREE.TextureLoader().load(url);
|
||||
const material = new THREE.SpriteMaterial({
|
||||
map: map,
|
||||
transparent: true,
|
||||
});
|
||||
const sprite = new THREE.Sprite(material);
|
||||
const [x, y] = offsetXY(point);
|
||||
sprite.scale.set(0.3, 0.3, 0.3);
|
||||
|
||||
sprite.position.set(x, -y, depth + 0.2);
|
||||
sprite.renderOrder = 1;
|
||||
|
||||
return sprite;
|
||||
};
|
||||
const setCenter = (map) => {
|
||||
map.rotation.x = -Math.PI / 2;
|
||||
const box = new THREE.Box3().setFromObject(map);
|
||||
const center = box.getCenter(new THREE.Vector3());
|
||||
|
||||
const offset = [0, 0];
|
||||
map.position.x = map.position.x - center.x - offset[0];
|
||||
map.position.z = map.position.z - center.z - offset[1];
|
||||
}
|
||||
THREE.OrbitControls = OrbitControls
|
||||
this.geoMap = new GeoMap();
|
||||
this.geoMap.init();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -200,6 +556,6 @@ export default {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.AppThreeMap{
|
||||
.AppThreeMap {
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -26,6 +26,10 @@ export default defineConfig({
|
||||
'/data-boot': {
|
||||
target: 'http://10.0.97.209',
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/presource': {
|
||||
target: 'http://10.0.97.209',
|
||||
changeOrigin: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user