3D地图提交一下
This commit is contained in:
@@ -3,8 +3,44 @@ import axios from 'axios'
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
|
|
||||||
window.axios = axios
|
window.axios = axios
|
||||||
const KENGEE_CDN_BASE = "http://10.0.97.209/presource/datascreen/"
|
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 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.$glob = {}
|
||||||
window.$dicts = dicts
|
window.$dicts = dicts
|
||||||
window.$waitFor = (target, t = 500) => new Promise(resolve => {
|
window.$waitFor = (target, t = 500) => new Promise(resolve => {
|
||||||
@@ -50,10 +86,5 @@ Vue.component("tableColumn", {
|
|||||||
|
|
||||||
export default Promise.all([
|
export default Promise.all([
|
||||||
import("./fetch"),
|
import("./fetch"),
|
||||||
...libs.map(url => new Promise(resolve => {
|
...libs.map(url => $loadScript('js', url)),
|
||||||
const script = document.createElement("script")
|
|
||||||
script.src = url
|
|
||||||
document.head.appendChild(script)
|
|
||||||
script.onload = () => resolve()
|
|
||||||
})),
|
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -1,196 +1,552 @@
|
|||||||
<script>
|
<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 {
|
export default {
|
||||||
name: "AppThreeMap",
|
name: "AppThreeMap",
|
||||||
label: "3D地图",
|
label: "3D地图",
|
||||||
mounted() {
|
mounted() {
|
||||||
const scene = new THREE.Scene()
|
THREE.OrbitControls = OrbitControls
|
||||||
const ambientLight = new THREE.AmbientLight(0xd4e7fd, 4);
|
this.geoMap = new GeoMap();
|
||||||
scene.add(ambientLight);
|
this.geoMap.init();
|
||||||
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];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@@ -200,6 +556,6 @@ export default {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.AppThreeMap{
|
.AppThreeMap {
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ export default defineConfig({
|
|||||||
'/data-boot': {
|
'/data-boot': {
|
||||||
target: 'http://10.0.97.209',
|
target: 'http://10.0.97.209',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
|
},
|
||||||
|
'/presource': {
|
||||||
|
target: 'http://10.0.97.209',
|
||||||
|
changeOrigin: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user