Bladeren bron

地球俯冲过渡动画删除绘制路径;优化过渡动画的加载速度

画安 4 dagen geleden
bovenliggende
commit
4d9859ad53
3 gewijzigde bestanden met toevoegingen van 206 en 30 verwijderingen
  1. 45 30
      src/components/CesiumTransition.vue
  2. 157 0
      src/utils/cesiumPreloader.js
  3. 4 0
      src/views/Login.vue

+ 45 - 30
src/components/CesiumTransition.vue

@@ -15,6 +15,7 @@
 </template>
 
 <script>
+import CesiumPreloader from '@/utils/cesiumPreloader';
 const Cesium = window.Cesium;
 
 function createOuterRingTexture(color) {
@@ -112,12 +113,15 @@ export default {
 
     this._coords = { start: [-15.0, 35.86, 45000000], china: [104.19, 35.86, 14000000] };
 
-    this.$nextTick(() => {
+    this.$nextTick(async () => {
       if (!this.$refs.cesiumContainer) return;
-      this.initCesium();
-      this.waitForGlobeReady().then(() => {
-        if (!this._destroyed) this.startTransition();
-      });
+      // 尝试复用预加载的 Viewer
+      const preloaded = await CesiumPreloader.acquire(this.$refs.cesiumContainer);
+      this.initCesium(preloaded);
+      if (!preloaded) {
+        await this.waitForGlobeReady();
+      }
+      if (!this._destroyed) this.startTransition();
     });
   },
   beforeDestroy() {
@@ -135,9 +139,11 @@ export default {
   methods: {
     waitForGlobeReady() {
       return new Promise(resolve => {
+        const startTime = Date.now();
+        const maxWait = 3000; // 最多等待3秒,避免卡住
         const check = () => {
           if (this._destroyed) { resolve(); return; }
-          if (this._viewer && this._viewer.scene.globe.tilesLoaded) {
+          if ((this._viewer && this._viewer.scene.globe.tilesLoaded) || (Date.now() - startTime > maxWait)) {
             resolve();
           } else {
             requestAnimationFrame(check);
@@ -147,7 +153,7 @@ export default {
       });
     },
 
-    initCesium() {
+    initCesium(preloadedViewer) {
       this._baseGold = Cesium.Color.GOLD;
       this._baseWhite = Cesium.Color.WHITE;
       this._baseBlack = Cesium.Color.BLACK;
@@ -164,15 +170,38 @@ export default {
         color: Cesium.Color.fromCssColorString(r.color)
       }));
 
-      this._viewer = new Cesium.Viewer(this.$refs.cesiumContainer, {
-        animation: false, timeline: false, baseLayerPicker: false, geocoder: false,
-        homeButton: false, sceneModePicker: false, navigationHelpButton: false, infoBox: false,
-        fullscreenButton: false, selectionIndicator: false,
-        imageryProvider: new Cesium.UrlTemplateImageryProvider({
-          url: './tiles/{z}/{y}/{x}.jpg',
-          maximumLevel: 12
-        })
-      });
+      if (preloadedViewer) {
+        // 复用预加载的 Viewer(瓦片已渲染好)
+        this._viewer = preloadedViewer;
+      } else {
+        // 降级:从头创建
+        this._viewer = new Cesium.Viewer(this.$refs.cesiumContainer, {
+          animation: false, timeline: false, baseLayerPicker: false, geocoder: false,
+          homeButton: false, sceneModePicker: false, navigationHelpButton: false, infoBox: false,
+          fullscreenButton: false, selectionIndicator: false, shadows: false, shouldAnimate: false,
+          requestRenderMode: false,
+          imageryProvider: new Cesium.UrlTemplateImageryProvider({
+            url: './tiles/{z}/{y}/{x}.jpg',
+            maximumLevel: 12
+          })
+        });
+
+        this._viewer.cesiumWidget.creditContainer.style.display = "none";
+
+        const scene = this._viewer.scene;
+        scene.fog.enabled = false;
+        scene.skyAtmosphere.show = false;
+        scene.globe.showGroundAtmosphere = false;
+        scene.globe.enableLighting = true;
+        scene.globe.tileCacheSize = 300;
+        scene.globe.maximumScreenSpaceError = 1.5;
+
+        this._viewer.clock.currentTime = Cesium.JulianDate.fromDate(new Date('2024-01-01T23:30:00Z'));
+        this._viewer.clock.shouldAnimate = false;
+
+        const baseLayer = this._viewer.imageryLayers.get(0);
+        if (baseLayer) { baseLayer.brightness = 0.45; baseLayer.contrast = 1.3; }
+      }
 
       const cam = this._viewer.scene.screenSpaceCameraController;
       cam.enableRotate = false;
@@ -181,14 +210,6 @@ export default {
       cam.enableTilt = false;
       cam.enableLook = false;
 
-      this._viewer.cesiumWidget.creditContainer.style.display = "none";
-      this._viewer.scene.globe.enableLighting = true;
-      this._viewer.clock.currentTime = Cesium.JulianDate.fromDate(new Date('2024-01-01T23:30:00Z'));
-      this._viewer.clock.shouldAnimate = false;
-
-      const baseLayer = this._viewer.imageryLayers.get(0);
-      if (baseLayer) { baseLayer.brightness = 0.45; baseLayer.contrast = 1.3; }
-
       // 微观区域高清卫星底图
       if (this.satelliteImage && this.satelliteImage.url) {
         const b = this.satelliteImage.bounds;
@@ -420,12 +441,6 @@ export default {
       this._microEffectsSource.show = true;
       if (this.poi && this.$refs.poiLabel) this.$refs.poiLabel.style.opacity = 1;
 
-      // 阶段4: 路网生长
-      await this._safeDelay(300);
-      if (this._destroyed) return;
-      const roadPromises = this._majorRoads.map((road, i) => this.animatePathGrowing(road, i * 150));
-      await Promise.all(roadPromises);
-      if (this._destroyed) return;
       await this._safeDelay(1000);
       if (this._destroyed) return;
       this.$emit('complete');

+ 157 - 0
src/utils/cesiumPreloader.js

@@ -0,0 +1,157 @@
+/**
+ * Cesium 预加载服务
+ * 在登录页提前创建隐藏的 Cesium Viewer,预渲染瓦片
+ * TransitionPage 挂载时直接复用已渲染好的实例
+ */
+const Cesium = window.Cesium;
+
+let _viewer = null;
+let _container = null;
+let _ready = false;
+let _readyCallbacks = [];
+
+const CesiumPreloader = {
+  /**
+   * 启动预加载(在 Login 页调用)
+   */
+  start() {
+    if (_viewer || _container) return; // 已启动
+
+    // 创建隐藏容器
+    _container = document.createElement('div');
+    _container.id = 'cesium-preload-container';
+    _container.style.cssText = 'position:fixed;top:0;left:0;width:100vw;height:100vh;z-index:-1;opacity:0;pointer-events:none;';
+    document.body.appendChild(_container);
+
+    _viewer = new Cesium.Viewer(_container, {
+      animation: false, timeline: false, baseLayerPicker: false, geocoder: false,
+      homeButton: false, sceneModePicker: false, navigationHelpButton: false, infoBox: false,
+      fullscreenButton: false, selectionIndicator: false, shadows: false, shouldAnimate: false,
+      imageryProvider: new Cesium.UrlTemplateImageryProvider({
+        url: './tiles/{z}/{y}/{x}.jpg',
+        maximumLevel: 12
+      })
+    });
+
+    _viewer.cesiumWidget.creditContainer.style.display = 'none';
+
+    const scene = _viewer.scene;
+    scene.fog.enabled = false;
+    scene.skyAtmosphere.show = false;
+    scene.globe.showGroundAtmosphere = false;
+    scene.globe.enableLighting = true;
+    scene.globe.tileCacheSize = 300;
+    scene.globe.maximumScreenSpaceError = 1.5;
+
+    _viewer.clock.currentTime = Cesium.JulianDate.fromDate(new Date('2024-01-01T23:30:00Z'));
+    _viewer.clock.shouldAnimate = false;
+
+    const baseLayer = _viewer.imageryLayers.get(0);
+    if (baseLayer) { baseLayer.brightness = 0.45; baseLayer.contrast = 1.3; }
+
+    // 设置初始视角并预加载瓦片
+    const startView = Cesium.Cartesian3.fromDegrees(-15.0, 35.86, 45000000);
+    _viewer.camera.setView({ destination: startView });
+
+    // 预加载:依次渲染初始视角和中国视角的瓦片
+    const chinaView = Cesium.Cartesian3.fromDegrees(104.19, 35.86, 14000000);
+    let phase = 0;
+
+    const check = () => {
+      if (!_viewer || _viewer.isDestroyed()) return;
+      if (_viewer.scene.globe.tilesLoaded) {
+        if (phase === 0) {
+          // 初始视角瓦片加载完,切到中国视角预加载
+          phase = 1;
+          _viewer.camera.setView({ destination: chinaView });
+          requestAnimationFrame(check);
+        } else {
+          // 中国视角也加载完,切回初始视角
+          _viewer.camera.setView({ destination: startView });
+          _ready = true;
+          _readyCallbacks.forEach(cb => cb());
+          _readyCallbacks = [];
+        }
+      } else {
+        requestAnimationFrame(check);
+      }
+    };
+    requestAnimationFrame(check);
+
+    // 超时保底:5秒后无论如何标记就绪
+    setTimeout(() => {
+      if (!_ready) {
+        _ready = true;
+        _readyCallbacks.forEach(cb => cb());
+        _readyCallbacks = [];
+      }
+    }, 5000);
+  },
+
+  /**
+   * 将预加载的 Viewer 移到目标容器(在 CesiumTransition 中调用)
+   * @param {HTMLElement} targetContainer 目标 DOM 容器
+   * @returns {Promise<Cesium.Viewer|null>}
+   */
+  acquire(targetContainer) {
+    return new Promise(resolve => {
+      const doAcquire = () => {
+        if (!_viewer || _viewer.isDestroyed()) {
+          resolve(null);
+          return;
+        }
+        // 将 canvas 容器移到目标 DOM
+        const cesiumWidget = _container.querySelector('.cesium-viewer');
+        if (cesiumWidget) {
+          targetContainer.appendChild(cesiumWidget);
+        }
+        // 移除隐藏容器
+        if (_container && _container.parentNode) {
+          _container.parentNode.removeChild(_container);
+        }
+        _container = null;
+
+        // 调整尺寸适配新容器
+        _viewer.resize();
+
+        const viewer = _viewer;
+        _viewer = null;
+        _ready = false;
+        resolve(viewer);
+      };
+
+      if (_ready) {
+        doAcquire();
+      } else if (_viewer) {
+        _readyCallbacks.push(doAcquire);
+      } else {
+        resolve(null); // 未启动预加载
+      }
+    });
+  },
+
+  /**
+   * 是否已启动预加载
+   */
+  isStarted() {
+    return !!_viewer;
+  },
+
+  /**
+   * 清理(如果未被 acquire 就离开了登录页)
+   */
+  destroy() {
+    if (_viewer && !_viewer.isDestroyed()) {
+      _viewer.destroy();
+    }
+    if (_container && _container.parentNode) {
+      _container.parentNode.removeChild(_container);
+    }
+    _viewer = null;
+    _container = null;
+    _ready = false;
+    _readyCallbacks = [];
+  }
+};
+
+export default CesiumPreloader;

+ 4 - 0
src/views/Login.vue

@@ -70,6 +70,10 @@ export default {
   // eslint-disable-next-line vue/multi-word-component-names
   name: "Login",
   components: { CaptchaCanvas },
+  created() {
+    // 提前预加载 Cesium 瓦片
+    import('@/utils/cesiumPreloader').then(m => m.default.start());
+  },
   data() {
     return {
       baseW: 1920,