|
|
@@ -5,9 +5,9 @@
|
|
|
<div
|
|
|
ref="poiLabel"
|
|
|
class="html-label"
|
|
|
- style="color: #00ffff; font-size: 20px; opacity: 0; display: none;"
|
|
|
+ :style="{ color: poi.fontColor || '#00ffff', fontSize: (poi.fontSize || 20) + 'px', opacity: 0, display: 'none' }"
|
|
|
>
|
|
|
- 鲁谷西街与政达路交叉口
|
|
|
+ {{ poi.label }}
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -47,6 +47,56 @@ const TWO_PI = Math.PI * 2;
|
|
|
|
|
|
export default {
|
|
|
name: 'CesiumTransition',
|
|
|
+ props: {
|
|
|
+ // 要高亮的省份名称
|
|
|
+ province: {
|
|
|
+ type: String,
|
|
|
+ default: '北京市'
|
|
|
+ },
|
|
|
+ // 微观标记点
|
|
|
+ poi: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({
|
|
|
+ lon: 116.22405,
|
|
|
+ lat: 39.90515,
|
|
|
+ label: '鲁谷西街与政达路交叉口',
|
|
|
+ radarRadius: 200,
|
|
|
+ themeColor: '#00bfff', // CSS 颜色字符串
|
|
|
+ fontColor: '#00ffff',
|
|
|
+ fontSize: 20
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 路网数据
|
|
|
+ roads: {
|
|
|
+ type: Array,
|
|
|
+ default: () => [
|
|
|
+ { name: "阜石路-阜成路快速路", width: 12, glowPower: 0.3, color: '#00ffff', path: [[116.18, 39.93], [116.22, 39.93], [116.26, 39.928], [116.30, 39.925]] },
|
|
|
+ { name: "石景山路-复兴路", width: 12, glowPower: 0.3, color: '#ff3333', path: [[116.18, 39.907], [116.224, 39.907], [116.25, 39.906], [116.30, 39.905]] },
|
|
|
+ { name: "莲石东路快速路", width: 12, glowPower: 0.3, color: '#00ff00', path: [[116.18, 39.89], [116.22, 39.888], [116.26, 39.885], [116.30, 39.88]] },
|
|
|
+ { name: "西五环路", width: 10, glowPower: 0.25, color: '#ffaa00', path: [[116.205, 39.95], [116.203, 39.92], [116.202, 39.89], [116.20, 39.86]] },
|
|
|
+ { name: "玉泉路", width: 10, glowPower: 0.25, color: '#cc00ff', path: [[116.25, 39.94], [116.25, 39.907], [116.248, 39.88], [116.245, 39.86]] },
|
|
|
+ { name: "西四环路", width: 10, glowPower: 0.25, color: '#ffff00', path: [[116.285, 39.94], [116.283, 39.91], [116.28, 39.88], [116.278, 39.85]] }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ // 微观区域卫星底图
|
|
|
+ satelliteImage: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({
|
|
|
+ url: './beijing-satellite.jpg',
|
|
|
+ bounds: [116.10, 39.80, 116.38, 39.98] // [west, south, east, north]
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 中国边界 GeoJSON 路径
|
|
|
+ boundaryUrl: {
|
|
|
+ type: String,
|
|
|
+ default: './china.json'
|
|
|
+ },
|
|
|
+ // 微观俯冲视角高度(米)
|
|
|
+ microViewRange: {
|
|
|
+ type: Number,
|
|
|
+ default: 10000
|
|
|
+ }
|
|
|
+ },
|
|
|
data() {
|
|
|
return {
|
|
|
isAnimating: false
|
|
|
@@ -63,7 +113,6 @@ export default {
|
|
|
this._pendingTimers = [];
|
|
|
|
|
|
this._coords = { start: [-15.0, 35.86, 45000000], china: [104.19, 35.86, 14000000] };
|
|
|
- this._poi = { lon: 116.22405, lat: 39.90515, radarRadius: 200 };
|
|
|
|
|
|
this.$nextTick(() => {
|
|
|
if (!this.$refs.cesiumContainer) return;
|
|
|
@@ -107,16 +156,13 @@ export default {
|
|
|
this._dynamicGold = this._baseGold.withAlpha(0);
|
|
|
this._dynamicWhite = this._baseWhite.withAlpha(0);
|
|
|
this._dynamicBlack = this._baseBlack.withAlpha(0);
|
|
|
- this._poiThemeColor = Cesium.Color.DEEPSKYBLUE;
|
|
|
+ this._poiThemeColor = Cesium.Color.fromCssColorString(this.poi.themeColor || '#00bfff');
|
|
|
|
|
|
- this._majorRoads = [
|
|
|
- { name: "阜石路-阜成路快速路", width: 12, glowPower: 0.3, color: Cesium.Color.fromCssColorString('#00ffff'), path: [[116.18, 39.93], [116.22, 39.93], [116.26, 39.928], [116.30, 39.925]] },
|
|
|
- { name: "石景山路-复兴路", width: 12, glowPower: 0.3, color: Cesium.Color.fromCssColorString('#ff3333'), path: [[116.18, 39.907], [116.224, 39.907], [116.25, 39.906], [116.30, 39.905]] },
|
|
|
- { name: "莲石东路快速路", width: 12, glowPower: 0.3, color: Cesium.Color.fromCssColorString('#00ff00'), path: [[116.18, 39.89], [116.22, 39.888], [116.26, 39.885], [116.30, 39.88]] },
|
|
|
- { name: "西五环路", width: 10, glowPower: 0.25, color: Cesium.Color.fromCssColorString('#ffaa00'), path: [[116.205, 39.95], [116.203, 39.92], [116.202, 39.89], [116.20, 39.86]] },
|
|
|
- { name: "玉泉路", width: 10, glowPower: 0.25, color: Cesium.Color.fromCssColorString('#cc00ff'), path: [[116.25, 39.94], [116.25, 39.907], [116.248, 39.88], [116.245, 39.86]] },
|
|
|
- { name: "西四环路", width: 10, glowPower: 0.25, color: Cesium.Color.fromCssColorString('#ffff00'), path: [[116.285, 39.94], [116.283, 39.91], [116.28, 39.88], [116.278, 39.85]] }
|
|
|
- ];
|
|
|
+ // 将 props 中的 roads 转为 Cesium 颜色对象
|
|
|
+ this._majorRoads = this.roads.map(r => ({
|
|
|
+ ...r,
|
|
|
+ color: Cesium.Color.fromCssColorString(r.color)
|
|
|
+ }));
|
|
|
|
|
|
this._viewer = new Cesium.Viewer(this.$refs.cesiumContainer, {
|
|
|
animation: false, timeline: false, baseLayerPicker: false, geocoder: false,
|
|
|
@@ -140,13 +186,16 @@ export default {
|
|
|
const baseLayer = this._viewer.imageryLayers.get(0);
|
|
|
if (baseLayer) { baseLayer.brightness = 0.45; baseLayer.contrast = 1.3; }
|
|
|
|
|
|
- // 微观区域高清卫星底图(本地静态图,秒加载,替代远程瓦片)
|
|
|
- this._viewer.imageryLayers.addImageryProvider(
|
|
|
- new Cesium.SingleTileImageryProvider({
|
|
|
- url: './beijing-satellite.jpg',
|
|
|
- rectangle: Cesium.Rectangle.fromDegrees(116.10, 39.80, 116.38, 39.98)
|
|
|
- })
|
|
|
- );
|
|
|
+ // 微观区域高清卫星底图
|
|
|
+ if (this.satelliteImage && this.satelliteImage.url) {
|
|
|
+ const b = this.satelliteImage.bounds;
|
|
|
+ this._viewer.imageryLayers.addImageryProvider(
|
|
|
+ new Cesium.SingleTileImageryProvider({
|
|
|
+ url: this.satelliteImage.url,
|
|
|
+ rectangle: Cesium.Rectangle.fromDegrees(b[0], b[1], b[2], b[3])
|
|
|
+ })
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
this._viewer.camera.setView({ destination: Cesium.Cartesian3.fromDegrees(...this._coords.start) });
|
|
|
|
|
|
@@ -156,7 +205,7 @@ export default {
|
|
|
this._preRenderListener = this._viewer.scene.preRender.addEventListener(() => {
|
|
|
if (!this._microEffectsSource || !this._microEffectsSource.show || !this.$refs.poiLabel) return;
|
|
|
if (this.$refs.poiLabel.style.opacity === "0") return;
|
|
|
- const pos3D = Cesium.Cartesian3.fromDegrees(this._poi.lon, this._poi.lat);
|
|
|
+ const pos3D = Cesium.Cartesian3.fromDegrees(this.poi.lon, this.poi.lat);
|
|
|
const screenPos = Cesium.SceneTransforms.wgs84ToWindowCoordinates(this._viewer.scene, pos3D);
|
|
|
if (screenPos && screenPos.y > 0) {
|
|
|
this.$refs.poiLabel.style.display = 'flex';
|
|
|
@@ -173,11 +222,9 @@ export default {
|
|
|
this._viewer.dataSources.add(this._chinaEffectsSource);
|
|
|
this._chinaEffectsSource.show = false;
|
|
|
|
|
|
- Cesium.GeoJsonDataSource.load('./china.json').then(ds => {
|
|
|
+ Cesium.GeoJsonDataSource.load(this.boundaryUrl).then(ds => {
|
|
|
if (this._destroyed) return;
|
|
|
- this._viewer.dataSources.add(ds);
|
|
|
this._chinaDataSource = ds;
|
|
|
- this._chinaDataSource.show = false;
|
|
|
|
|
|
const entities = ds.entities.values;
|
|
|
const provinceMainLand = {};
|
|
|
@@ -195,7 +242,7 @@ export default {
|
|
|
color: new Cesium.CallbackProperty(() => this._dynamicGold, false)
|
|
|
})
|
|
|
});
|
|
|
- if (entity.name === '北京市') {
|
|
|
+ if (entity.name === this.province) {
|
|
|
const pointCount = positions.length;
|
|
|
if (!provinceMainLand[entity.name] || pointCount > provinceMainLand[entity.name].pointCount) {
|
|
|
provinceMainLand[entity.name] = { pointCount, center: Cesium.BoundingSphere.fromPoints(positions).center };
|
|
|
@@ -204,7 +251,6 @@ export default {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 实体处理完毕后再添加到 viewer
|
|
|
this._viewer.dataSources.add(ds);
|
|
|
|
|
|
for (const name in provinceMainLand) {
|
|
|
@@ -244,15 +290,15 @@ export default {
|
|
|
this._viewer.dataSources.add(this._microEffectsSource);
|
|
|
this._microEffectsSource.show = false;
|
|
|
|
|
|
- const pos3D = Cesium.Cartesian3.fromDegrees(this._poi.lon, this._poi.lat);
|
|
|
+ const pos3D = Cesium.Cartesian3.fromDegrees(this.poi.lon, this.poi.lat);
|
|
|
const slowRingImg = createOuterRingTexture(this._poiThemeColor);
|
|
|
const fastScannerImg = createScannerTexture(this._poiThemeColor);
|
|
|
const beamImg = createLightBeamTexture(this._poiThemeColor);
|
|
|
|
|
|
- this._microEffectsSource.entities.add({ position: pos3D, ellipse: { semiMinorAxis: this._poi.radarRadius, semiMajorAxis: this._poi.radarRadius, material: new Cesium.ImageMaterialProperty({ image: slowRingImg, transparent: true }), stRotation: new Cesium.CallbackProperty(() => (Date.now() / 5000.0) % TWO_PI, false), height: 20 } });
|
|
|
- this._microEffectsSource.entities.add({ position: pos3D, ellipse: { semiMinorAxis: this._poi.radarRadius, semiMajorAxis: this._poi.radarRadius, material: new Cesium.ImageMaterialProperty({ image: fastScannerImg, transparent: true }), stRotation: new Cesium.CallbackProperty(() => (Date.now() / 300.0) % TWO_PI, false), height: 21 } });
|
|
|
- this._microEffectsSource.entities.add({ position: Cesium.Cartesian3.fromDegrees(this._poi.lon, this._poi.lat, 2000.0), cylinder: { length: 4000, topRadius: 20, bottomRadius: 200, material: new Cesium.ImageMaterialProperty({ image: beamImg, transparent: true }) } });
|
|
|
- this._microEffectsSource.entities.add({ position: Cesium.Cartesian3.fromDegrees(this._poi.lon, this._poi.lat, 50), point: { pixelSize: 15, color: Cesium.Color.WHITE, outlineColor: this._poiThemeColor, outlineWidth: 3 } });
|
|
|
+ this._microEffectsSource.entities.add({ position: pos3D, ellipse: { semiMinorAxis: this.poi.radarRadius, semiMajorAxis: this.poi.radarRadius, material: new Cesium.ImageMaterialProperty({ image: slowRingImg, transparent: true }), stRotation: new Cesium.CallbackProperty(() => (Date.now() / 5000.0) % TWO_PI, false), height: 20 } });
|
|
|
+ this._microEffectsSource.entities.add({ position: pos3D, ellipse: { semiMinorAxis: this.poi.radarRadius, semiMajorAxis: this.poi.radarRadius, material: new Cesium.ImageMaterialProperty({ image: fastScannerImg, transparent: true }), stRotation: new Cesium.CallbackProperty(() => (Date.now() / 300.0) % TWO_PI, false), height: 21 } });
|
|
|
+ this._microEffectsSource.entities.add({ position: Cesium.Cartesian3.fromDegrees(this.poi.lon, this.poi.lat, 2000.0), cylinder: { length: 4000, topRadius: 20, bottomRadius: 200, material: new Cesium.ImageMaterialProperty({ image: beamImg, transparent: true }) } });
|
|
|
+ this._microEffectsSource.entities.add({ position: Cesium.Cartesian3.fromDegrees(this.poi.lon, this.poi.lat, 50), point: { pixelSize: 15, color: Cesium.Color.WHITE, outlineColor: this._poiThemeColor, outlineWidth: 3 } });
|
|
|
},
|
|
|
|
|
|
fadeMacroLayer(startAlpha, endAlpha, durationMs) {
|
|
|
@@ -334,7 +380,7 @@ export default {
|
|
|
if (this.isAnimating) return;
|
|
|
this.isAnimating = true;
|
|
|
|
|
|
- // 阶段1: 飞向中国 (2s),飞行0.8s后开始淡入边界
|
|
|
+ // 阶段1: 飞向中国 (2s)
|
|
|
this._viewer.camera.flyTo({
|
|
|
destination: Cesium.Cartesian3.fromDegrees(...this._coords.china), duration: 2
|
|
|
});
|
|
|
@@ -346,10 +392,10 @@ export default {
|
|
|
await this._safeDelay(3000);
|
|
|
if (this._destroyed) return;
|
|
|
|
|
|
- // 阶段2: 俯冲到微观 (2s),同时淡出边界
|
|
|
- const targetSphere = Cesium.BoundingSphere.fromPoints([Cesium.Cartesian3.fromDegrees(this._poi.lon, this._poi.lat)]);
|
|
|
+ // 阶段2: 俯冲到微观
|
|
|
+ const targetSphere = Cesium.BoundingSphere.fromPoints([Cesium.Cartesian3.fromDegrees(this.poi.lon, this.poi.lat)]);
|
|
|
this._viewer.camera.flyToBoundingSphere(targetSphere, {
|
|
|
- offset: new Cesium.HeadingPitchRange(0.0, Cesium.Math.toRadians(-45), 10000.0),
|
|
|
+ offset: new Cesium.HeadingPitchRange(0.0, Cesium.Math.toRadians(-45), this.microViewRange),
|
|
|
duration: 2, easingFunction: Cesium.EasingFunction.CUBIC_IN_OUT
|
|
|
});
|
|
|
this.fadeMacroLayer(1.0, 0.0, 800);
|
|
|
@@ -360,7 +406,7 @@ export default {
|
|
|
this._microEffectsSource.show = true;
|
|
|
if (this.$refs.poiLabel) this.$refs.poiLabel.style.opacity = 1;
|
|
|
|
|
|
- // 阶段4: 路网生长 (每条间隔150ms),全部画完立即结束
|
|
|
+ // 阶段4: 路网生长
|
|
|
await this._safeDelay(300);
|
|
|
if (this._destroyed) return;
|
|
|
const roadPromises = this._majorRoads.map((road, i) => this.animatePathGrowing(road, i * 150));
|