Просмотр исходного кода

场景属性修改组件时间轴功能修复

WQQ 1 неделя назад
Родитель
Сommit
d0a00247d8

+ 1 - 1
RuoYi-Vue3/public/Cesium/Widgets/Timeline/Timeline.css

@@ -1 +1 @@
-.cesium-timeline-main{position:relative;left:0;bottom:0;overflow:hidden;border:solid 1px #888}.cesium-timeline-trackContainer{width:100%;overflow:auto;border-top:solid 1px #888;position:relative;top:0;left:0}.cesium-timeline-tracks{position:absolute;top:0;left:0;width:100%}.cesium-timeline-needle{position:absolute;left:0;top:1.7em;bottom:0;width:1px;background:red}.cesium-timeline-bar{position:relative;left:0;top:0;overflow:hidden;cursor:pointer;width:100%;height:1.7em;background:linear-gradient(to bottom,rgba(116,117,119,.8) 0,rgba(58,68,82,.8) 11%,rgba(46,50,56,.8) 46%,rgba(53,53,53,.8) 81%,rgba(53,53,53,.8) 100%)}.cesium-timeline-ruler{visibility:hidden;white-space:nowrap;font-size:80%;z-index:-200}.cesium-timeline-highlight{position:absolute;bottom:0;left:0;background:#08f}.cesium-timeline-ticLabel{position:absolute;top:0;left:0;white-space:nowrap;font-size:80%;color:#eee}.cesium-timeline-ticMain{position:absolute;bottom:0;left:0;width:1px;height:50%;background:#eee}.cesium-timeline-ticSub{position:absolute;bottom:0;left:0;width:1px;height:33%;background:#aaa}.cesium-timeline-ticTiny{position:absolute;bottom:0;left:0;width:1px;height:25%;background:#888}.cesium-timeline-icon16{display:block;position:absolute;width:16px;height:16px;background-image:url(../Images/TimelineIcons.png);background-repeat:no-repeat}
+.cesium-timeline-main{position:relative;left:0;bottom:0;overflow:hidden;border:solid 1px #888}.cesium-timeline-trackContainer{width:100%;overflow:auto;border-top:solid 1px #888;position:relative;top:0;left:0}.cesium-timeline-tracks{position:absolute;top:0;left:0;width:100%}.cesium-timeline-needle{position:absolute;left:0;top:2em;bottom:0;width:2px;background:#ff4500;border-radius:1px}.cesium-timeline-bar{position:relative;left:0;top:0;overflow:hidden;cursor:pointer;width:100%;height:2em;background:linear-gradient(to bottom,rgba(60,100,150,.9) 0,rgba(40,80,120,.9) 50%,rgba(30,60,90,.9) 100%);border-bottom:1px solid #444;box-shadow:0 1px 3px rgba(0,0,0,0.3)}.cesium-timeline-ruler{visibility:hidden;white-space:nowrap;font-size:80%;z-index:-200}.cesium-timeline-highlight{position:absolute;bottom:0;left:0;background:#08f;opacity:0.6}.cesium-timeline-ticLabel{position:absolute;top:0;left:0;white-space:nowrap;font-size:10px;color:#fff;text-shadow:1px 1px 2px rgba(0,0,0,0.8);font-weight:bold}.cesium-timeline-ticMain{position:absolute;bottom:0;left:0;width:2px;height:60%;background:#fff}.cesium-timeline-ticSub{position:absolute;bottom:0;left:0;width:1px;height:40%;background:#aaa}.cesium-timeline-ticTiny{position:absolute;bottom:0;left:0;width:1px;height:25%;background:#888}.cesium-timeline-icon16{display:block;position:absolute;width:16px;height:16px;bottom:0;background-image:url(../Images/TimelineIcons.png);background-repeat:no-repeat}

+ 215 - 0
RuoYi-Vue3/src/supermap-cesium-module/components/scene/custom-timeline/custom-timeline.vue

@@ -0,0 +1,215 @@
+<template>
+  <div class="custom-timeline-container" v-show="visible">
+    <div class="custom-timeline" ref="timelineRef" @mousedown="handleMouseDown" @mousemove="handleMouseMove" @mouseup="handleMouseUp" @mouseleave="handleMouseUp">
+      <div class="timeline-track">
+        <!-- 时间刻度 -->
+        <div class="timeline-tick" v-for="hour in 25" :key="hour" :style="{ left: `${(hour - 1) * (100/24)}%` }">
+          <div class="tick-mark"></div>
+          <div class="tick-label">{{ formatHour(hour - 1) }}</div>
+        </div>
+        <!-- 滑块 -->
+        <div class="timeline-handle" ref="handleRef" :style="{ left: sliderPosition + '%' }" @mousedown.stop="handleHandleMouseDown"></div>
+        <!-- 指针 -->
+        <div class="timeline-needle" :style="{ left: sliderPosition + '%' }"></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue';
+
+export default {
+  name: 'CustomTimeline',
+  props: {
+    visible: {
+      type: Boolean,
+      default: false
+    },
+    currentTime: {
+      type: Date,
+      default: () => new Date()
+    }
+  },
+  emits: ['timeChange'],
+  setup(props, { emit }) {
+    const timelineRef = ref(null);
+    const handleRef = ref(null);
+    const isDragging = ref(false);
+    const sliderPosition = ref(0);
+    
+    // 计算滑块位置(0-100%)
+    const updateSliderPosition = (date) => {
+      const hours = date.getHours();
+      const minutes = date.getMinutes();
+      const seconds = date.getSeconds();
+      const totalSeconds = hours * 3600 + minutes * 60 + seconds;
+      const percentage = (totalSeconds / 86400) * 100;
+      sliderPosition.value = percentage;
+    };
+    
+    // 格式化小时显示
+    const formatHour = (hour) => {
+      return `${hour.toString().padStart(2, '0')}:00`;
+    };
+    
+    // 处理鼠标按下事件
+    const handleMouseDown = (e) => {
+      if (!timelineRef.value) return;
+      
+      const rect = timelineRef.value.getBoundingClientRect();
+      const x = e.clientX - rect.left;
+      const percentage = (x / rect.width) * 100;
+      updateTimeFromPercentage(percentage);
+      isDragging.value = true;
+    };
+    
+    // 处理鼠标移动事件
+    const handleMouseMove = (e) => {
+      if (!isDragging.value || !timelineRef.value) return;
+      
+      const rect = timelineRef.value.getBoundingClientRect();
+      const x = e.clientX - rect.left;
+      let percentage = (x / rect.width) * 100;
+      
+      // 限制在0-100%范围内
+      percentage = Math.max(0, Math.min(100, percentage));
+      updateTimeFromPercentage(percentage);
+    };
+    
+    // 处理鼠标释放事件
+    const handleMouseUp = () => {
+      isDragging.value = false;
+    };
+    
+    // 处理滑块鼠标按下事件
+    const handleHandleMouseDown = (e) => {
+      e.stopPropagation();
+      isDragging.value = true;
+    };
+    
+    // 根据百分比更新时间
+    const updateTimeFromPercentage = (percentage) => {
+      const totalSeconds = (percentage / 100) * 86400;
+      const hours = Math.floor(totalSeconds / 3600);
+      const minutes = Math.floor((totalSeconds % 3600) / 60);
+      const seconds = Math.floor(totalSeconds % 60);
+      
+      const newDate = new Date();
+      newDate.setHours(hours, minutes, seconds, 0);
+      
+      sliderPosition.value = percentage;
+      emit('timeChange', newDate);
+    };
+    
+    // 监听当前时间变化
+    watch(() => props.currentTime, (newTime) => {
+      updateSliderPosition(newTime);
+    }, { immediate: true });
+    
+    // 监听可见性变化
+    watch(() => props.visible, (newVisible) => {
+      if (newVisible) {
+        // 确保时间轴可见时更新位置
+        setTimeout(() => {
+          updateSliderPosition(props.currentTime);
+        }, 100);
+      }
+    });
+    
+    // 清理事件监听器
+    onBeforeUnmount(() => {
+      isDragging.value = false;
+    });
+    
+    return {
+      timelineRef,
+      handleRef,
+      isDragging,
+      sliderPosition,
+      formatHour,
+      handleMouseDown,
+      handleMouseMove,
+      handleMouseUp,
+      handleHandleMouseDown
+    };
+  }
+};
+</script>
+
+<style scoped>
+.custom-timeline-container {
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  z-index: 9999;
+  background: rgba(30, 60, 90, 0.9);
+  border-top: 1px solid #444;
+  box-shadow: 0 -1px 3px rgba(0, 0, 0, 0.3);
+}
+
+.custom-timeline {
+  height: 2em;
+  position: relative;
+  cursor: pointer;
+}
+
+.timeline-track {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  background: linear-gradient(to bottom, rgba(60, 100, 150, 0.9) 0, rgba(40, 80, 120, 0.9) 50%, rgba(30, 60, 90, 0.9) 100%);
+}
+
+.timeline-tick {
+  position: absolute;
+  top: 0;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.tick-mark {
+  width: 1px;
+  height: 60%;
+  background: #fff;
+}
+
+.tick-label {
+  font-size: 10px;
+  color: #fff;
+  text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.8);
+  font-weight: bold;
+  margin-top: 2px;
+}
+
+.timeline-handle {
+  position: absolute;
+  top: 0;
+  width: 16px;
+  height: 16px;
+  background-image: url('../../../../public/Cesium/Widgets/Images/TimelineIcons.png');
+  background-repeat: no-repeat;
+  background-position: 0 0;
+  transform: translateX(-50%);
+  cursor: pointer;
+  z-index: 10;
+}
+
+.timeline-handle:hover {
+  background-position: -16px 0;
+}
+
+.timeline-needle {
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  width: 2px;
+  background: #ff4500;
+  border-radius: 1px;
+  transform: translateX(-50%);
+  z-index: 5;
+}
+</style>

+ 245 - 185
RuoYi-Vue3/src/supermap-cesium-module/components/scene/scene-attribute/scene-attribute.js

@@ -1,11 +1,15 @@
 // 引入依赖
-import { watch, ref, reactive, toRefs, onBeforeUnmount, onMounted } from "vue";
+import { watch, ref, reactive, toRefs, onBeforeUnmount, onMounted, nextTick } from "vue";
 import { storeState } from '../../../js/store/store.js'   
 import tool from '../../../js/tool/tool.js'        
 import resource from '../../../js/local/lang.js'  
 
 function s3mlayerAttribute(props) {
-    let viewer = window.viewer;
+    const viewer = window.viewer;
+
+    // 设置默认时间为上午10:00
+    const defaultTime = new Date();
+    defaultTime.setHours(10, 0, 0, 0);
 
     let state = reactive({
         earthShow: true,
@@ -22,7 +26,7 @@ function s3mlayerAttribute(props) {
         rain: false,
         snow: false,
         surfaceTransparency: 1,
-        cloudsUrl: '/img/skyboxs/clouds/clouds1.png',
+        cloudsUrl: 'public/img/skyboxs/clouds/clouds1.png',
         skyboxSources: null,
         skyboxType: 'bluesky',
         uspeed: 0,
@@ -36,234 +40,288 @@ function s3mlayerAttribute(props) {
         snowAngle: 10,
         snowType: '0',
         compass: false,
-        isCompass: ''
+        isCompass: '',
+        currentTime: defaultTime
     });
 
     state.skyboxSources = {
         bluesky: {
-            positiveX: '/img/skyboxs/bluesky/Right.jpg',
-            negativeX: '/img/skyboxs/bluesky/Left.jpg',
-            positiveY: '/img/skyboxs/bluesky/Front.jpg',
-            negativeY: '/img/skyboxs/bluesky/Back.jpg',
-            positiveZ: '/img/skyboxs/bluesky/Up.jpg',
-            negativeZ: '/img/skyboxs/bluesky/Down.jpg'
+            positiveX: 'public/img/skyboxs/bluesky/Right.jpg',
+            negativeX: 'public/img/skyboxs/bluesky/Left.jpg',
+            positiveY: 'public/img/skyboxs/bluesky/Front.jpg',
+            negativeY: 'public/img/skyboxs/bluesky/Back.jpg',
+            positiveZ: 'public/img/skyboxs/bluesky/Up.jpg',
+            negativeZ: 'public/img/skyboxs/bluesky/Down.jpg'
         },
         sunsetglow: {
-            positiveX: '/img/skyboxs/sunsetglow/Right.jpg',
-            negativeX: '/img/skyboxs/sunsetglow/Left.jpg',
-            positiveY: '/img/skyboxs/sunsetglow/Front.jpg',
-            negativeY: '/img/skyboxs/sunsetglow/Back.jpg',
-            positiveZ: '/img/skyboxs/sunsetglow/Up.jpg',
-            negativeZ: '/img/skyboxs/sunsetglow/Down.jpg'
+            positiveX: 'public/img/skyboxs/sunsetglow/Right.jpg',
+            negativeX: 'public/img/skyboxs/sunsetglow/Left.jpg',
+            positiveY: 'public/img/skyboxs/sunsetglow/Front.jpg',
+            negativeY: 'public/img/skyboxs/sunsetglow/Back.jpg',
+            positiveZ: 'public/img/skyboxs/sunsetglow/Up.jpg',
+            negativeZ: 'public/img/skyboxs/sunsetglow/Down.jpg'
         }
     };
 
     if (props) {
         for (let key in props) {
-            if (state.hasOwnProperty(key)) {
-                state[key] = props[key]
-            } else {
-                tool.Message.errorMsg(resource.AttributeError + key);
-            }
+            if (state.hasOwnProperty(key)) state[key] = props[key];
+            else tool.Message.errorMsg(resource.AttributeError + key);
         }
     }
 
-    let cloudBox = null;
-    let skyboxs = {};
-    let defaultSkybox = null;
-
-    // ==============================================
-    // 【极简、安全、不报错】创建天空盒
-    // ==============================================
-    function initSkyBoxWhenViewerReady() {
-        if (!viewer || !viewer.scene || !Cesium.SkyBox) return;
-        if (Object.keys(skyboxs).length > 0) return;
-
-        try {
-            for (let key in state.skyboxSources) {
-                let sb = new Cesium.SkyBox({
-                    sources: state.skyboxSources[key]
-                });
-                sb.USpeed = state.uspeed;
-                sb.VSpeed = state.vspeed;
-                sb.WSpeed = state.wspeed;
-                skyboxs[key] = sb;
-            }
-            defaultSkybox = viewer.scene.skyBox;
-        } catch (e) {}
+    let cloudBox = new Cesium.CloudBox({ url: state.cloudsUrl });
+    let skyboxs = {}, defaultSkybox;
+    for (let key in state.skyboxSources) {
+        let skybox = new Cesium.SkyBox({ sources: state.skyboxSources[key] });
+        skybox.USpeed = state.uspeed;
+        skybox.VSpeed = state.vspeed;
+        skybox.WSpeed = state.wspeed;
+        skyboxs[key] = skybox;
     }
 
-    // ==============================================
-    // 【你原版正常的雨雪】
-    // ==============================================
-    function createWeatherEffects() {
-        if (!viewer || !viewer.scene || !viewer.scene.postProcessStages) return;
-
-        if (!viewer.scene.postProcessStages.rain) {
-            const rainStage = new Cesium.PostProcessStage({
-                name: 'rain',
-                fragmentShader: `
-                    uniform sampler2D colorTexture;
-                    varying vec2 v_textureCoordinates;
-                    uniform float angle;
-                    uniform float speed;
-                    uniform float time;
-                    float random(vec2 st) { return fract(sin(dot(st, vec2(12.9898,78.233)))*43758.5453123); }
-                    void main() {
-                        vec4 color = texture2D(colorTexture, v_textureCoordinates);
-                        float t = time * speed;
-                        float angleRad = angle * 3.14159/180.0;
-                        mat2 rot = mat2(cos(angleRad), -sin(angleRad), sin(angleRad), cos(angleRad));
-                        vec2 uv = rot * (v_textureCoordinates * 12.0);
-                        uv.y += t * 0.01;
-                        float rain = 0.0;
-                        for(int i=0; i<3; i++){
-                            vec2 g = fract(uv * vec2(1.0, 25.0)) - 0.5;
-                            float d = length(g);
-                            rain += smoothstep(0.05, 0.0, d);
-                            uv *= 1.6;
-                        }
-                        color.rgb += rain * 0.12;
-                        gl_FragColor = color;
-                    }
-                `,
-                uniforms: { angle: state.rainAngle, speed: state.rainSpeed, time: 0 }
-            });
-            viewer.scene.postProcessStages.add(rainStage);
-            viewer.scene.preRender.addEventListener(() => {
-                if (viewer.scene.postProcessStages.rain)
-                    viewer.scene.postProcessStages.rain.uniforms.time = performance.now() * 0.001;
-            });
+    function initialSkyBox() {
+        if (viewer.scene.frameState.passes.render) {
+            for (let key in skyboxs) {
+                skyboxs[key].update(viewer.scene.frameState, true);
+                skyboxs[key].show = false;
+            }
+            viewer.scene.postRender.removeEventListener(initialSkyBox);
         }
+    }
 
-        if (!viewer.scene.postProcessStages.snow) {
-            const snowStage = new Cesium.PostProcessStage({
-                name: 'snow',
-                fragmentShader: `
-                    uniform sampler2D colorTexture;
-                    varying vec2 v_textureCoordinates;
-                    uniform float density;
-                    uniform float speed;
-                    uniform float angle;
-                    uniform float time;
-                    float random(vec2 st) { return fract(sin(dot(st, vec2(12.9898,78.233)))*43758.5453123); }
-                    void main() {
-                        vec4 color = texture2D(colorTexture, v_textureCoordinates);
-                        float t = time * speed;
-                        float angleRad = angle * 3.14159/180.0;
-                        mat2 rot = mat2(cos(angleRad), -sin(angleRad), sin(angleRad), cos(angleRad));
-                        vec2 uv = rot * (v_textureCoordinates * 8.0);
-                        uv.y += t * 0.004;
-                        float snow = 0.0;
-                        for(int i=0; i<3; i++){
-                            vec2 g = fract(uv * vec2(1.0, 15.0)) - 0.5;
-                            float d = length(g);
-                            snow += smoothstep(0.08, 0.0, d);
-                            uv *= 1.5;
-                        }
-                        color.rgb += snow * density * 0.015;
-                        gl_FragColor = color;
-                    }
-                `,
-                uniforms: { density: state.snowDesity, speed: state.snowSpeed, angle: state.snowAngle, time: 0 }
-            });
-            viewer.scene.postProcessStages.add(snowStage);
-            viewer.scene.preRender.addEventListener(() => {
-                if (viewer.scene.postProcessStages.snow)
-                    viewer.scene.postProcessStages.snow.uniforms.time = performance.now() * 0.001;
-            });
+    function watchCameraHeight() {
+        if (state.skyBox) {
+            let h = viewer.scene.camera.positionCartographic.height;
+            viewer.scene.skyBox.show = h < 22e4;
+            state.atomsphereRender = h >= 22e4;
         }
     }
 
     // ==============================================
-    // 【最关键:强制显示天空盒,不搞监听,不搞高度判断】
+    // ✅【终极正确】24小时时间轴(00:00-24:00)
     // ==============================================
-    watch(() => state.skyBox, (val) => {
-        if (!viewer || !viewer.scene) return;
+    function initCorrectBeijingTime() {
+        if (!viewer) return;
 
-        initSkyBoxWhenViewerReady();
-        let target = skyboxs[state.skyboxType];
-        if (!target) return;
+        viewer.scene.sun.show = true;
+        viewer.scene.moon.show = true;
+        viewer.scene.globe.enableLighting = true;
 
-        if (val) {
-            // 强制显示!
-            viewer.scene.skyAtmosphere.show = false;
-            state.atomsphereRender = false;
-            viewer.scene.skyBox = target;
-            viewer.scene.skyBox.show = true;
+        // 设置时间轴为今天的00:00到24:00
+        const today = new Date();
+        today.setHours(0, 0, 0, 0);
+        const tomorrow = new Date(today);
+        tomorrow.setDate(tomorrow.getDate() + 1);
+
+        // 设置默认时间为上午10:00
+        const defaultTime = new Date(today);
+        defaultTime.setHours(10, 0, 0, 0);
+
+        const start = Cesium.JulianDate.fromDate(today);
+        const end = Cesium.JulianDate.fromDate(tomorrow);
+        const current = Cesium.JulianDate.fromDate(defaultTime);
+
+        viewer.clock.startTime = start;
+        viewer.clock.currentTime = current;
+        viewer.clock.stopTime = end;
+        viewer.clock.multiplier = 1;
+        viewer.clock.shouldAnimate = false;
+
+        // 监听时间变化,更新天空效果
+        viewer.clock.onTick.addEventListener(function(clock) {
+            updateSkyEffect(clock.currentTime);
+            state.currentTime = Cesium.JulianDate.toDate(clock.currentTime);
+        });
+    }
+
+    // 根据时间更新天空效果
+    function updateSkyEffect(currentTime) {
+        if (!viewer) return;
+
+        // 获取当前时间的小时数(北京时间)
+        const date = Cesium.JulianDate.toDate(currentTime);
+        const hour = date.getHours();
+
+        // 根据小时数设置不同的天空效果
+        if (hour >= 0 && hour < 3) {
+            // 凌晨00点-3点:漆黑
+            viewer.scene.skyAtmosphere.hueShift = -0.3;
+            viewer.scene.skyAtmosphere.saturationShift = -0.6;
+            viewer.scene.skyAtmosphere.brightnessShift = -0.7;
+        } else if (hour >= 3 && hour < 6) {
+            // 凌晨3点-6点:天空逐渐转亮
+            const progress = (hour - 3) / 3;
+            viewer.scene.skyAtmosphere.hueShift = -0.3 + progress * 0.3;
+            viewer.scene.skyAtmosphere.saturationShift = -0.6 + progress * 0.6;
+            viewer.scene.skyAtmosphere.brightnessShift = -0.7 + progress * 0.7;
+        } else if (hour >= 6 && hour < 8) {
+            // 早上6点-8点:日出
+            const progress = (hour - 6) / 2;
+            viewer.scene.skyAtmosphere.hueShift = 0 + progress * 0.05;
+            viewer.scene.skyAtmosphere.saturationShift = 0 + progress * 0.15;
+            viewer.scene.skyAtmosphere.brightnessShift = 0 + progress * 0.15;
+        } else if (hour >= 8 && hour < 12) {
+            // 早上8点-12点:上午
+            const progress = (hour - 8) / 4;
+            viewer.scene.skyAtmosphere.hueShift = 0.05 + progress * 0.05;
+            viewer.scene.skyAtmosphere.saturationShift = 0.15 + progress * 0.05;
+            viewer.scene.skyAtmosphere.brightnessShift = 0.15 + progress * 0.05;
+        } else if (hour >= 12 && hour < 16) {
+            // 中午12点-16点:中午
+            viewer.scene.skyAtmosphere.hueShift = 0.1;
+            viewer.scene.skyAtmosphere.saturationShift = 0.2;
+            viewer.scene.skyAtmosphere.brightnessShift = 0.2;
+        } else if (hour >= 16 && hour < 18) {
+            // 下午16点-18点:日落
+            const progress = (hour - 16) / 2;
+            viewer.scene.skyAtmosphere.hueShift = 0.1 + progress * 0.1;
+            viewer.scene.skyAtmosphere.saturationShift = 0.2 + progress * 0.1;
+            viewer.scene.skyAtmosphere.brightnessShift = 0.2 - progress * 0.2;
+        } else if (hour >= 18 && hour < 21) {
+            // 傍晚18点-21点:天空逐渐转暗
+            const progress = (hour - 18) / 3;
+            viewer.scene.skyAtmosphere.hueShift = 0.2 + progress * 0.1;
+            viewer.scene.skyAtmosphere.saturationShift = 0.3 - progress * 0.4;
+            viewer.scene.skyAtmosphere.brightnessShift = 0 - progress * 0.5;
         } else {
-            viewer.scene.skyBox = defaultSkybox;
-            viewer.scene.skyAtmosphere.show = true;
-            state.atomsphereRender = true;
+            // 晚上21点-24点:漆黑
+            viewer.scene.skyAtmosphere.hueShift = -0.3;
+            viewer.scene.skyAtmosphere.saturationShift = -0.6;
+            viewer.scene.skyAtmosphere.brightnessShift = -0.7;
         }
-    });
+    }
 
-    watch(() => state.skyboxType, (val) => {
-        if (!state.skyBox || !viewer || !viewer.scene) return;
-        initSkyBoxWhenViewerReady();
-        let target = skyboxs[val];
-        if (target) {
-            viewer.scene.skyBox = target;
-            viewer.scene.skyBox.show = true;
-        }
-    });
+    // 处理时间轴时间变化
+    function handleTimeChange(newTime) {
+        if (!viewer) return;
+        state.currentTime = newTime;
+        const julianTime = Cesium.JulianDate.fromDate(newTime);
+        viewer.clock.currentTime = julianTime;
+        updateSkyEffect(julianTime);
+    }
 
-    // ========================
-    // 以下全部保留你正常能用的代码
-    // ========================
     if (storeState.isViewer) {
-        initSkyBoxWhenViewerReady();
-        createWeatherEffects();
+        defaultSkybox = viewer.scene.skyBox;
+        viewer.scene.postRender.addEventListener(initialSkyBox);
+        viewer.scene.postProcessStages.snow.uniforms.density = parseFloat(state.snowDesity);
+        viewer.scene.postProcessStages.snow.uniforms.speed = parseFloat(state.snowSpeed);
+        viewer.scene.postProcessStages.rain.uniforms.speed = parseFloat(state.rainSpeed);
+        viewer.scene.debugShowFramesPerSecond = state.frameRate;
+        watchCameraHeight();
+        initCorrectBeijingTime();
     }
+
     watch(() => storeState.isViewer, val => {
         if (val) {
-            viewer = window.viewer;
-            initSkyBoxWhenViewerReady();
-            createWeatherEffects();
+            defaultSkybox = viewer.scene.skyBox;
+            viewer.scene.postRender.addEventListener(initialSkyBox);
+            viewer.scene.postProcessStages.snow.uniforms.density = parseFloat(state.snowDesity);
+            viewer.scene.postProcessStages.snow.uniforms.speed = parseFloat(state.snowSpeed);
+            viewer.scene.postProcessStages.rain.uniforms.speed = parseFloat(state.rainSpeed);
+            viewer.scene.debugShowFramesPerSecond = state.frameRate;
+            watchCameraHeight();
+            initCorrectBeijingTime();
         }
     });
 
     onBeforeUnmount(() => {
-        try {
-            if (cloudBox) cloudBox.destroy();
-            for (let k in skyboxs) skyboxs[k].destroy();
-        } catch (e) {}
+        cloudBox.destroy();
+        for (let key in skyboxs) skyboxs[key].destroy();
+        viewer.scene.skyBox = defaultSkybox;
     });
 
-    watch(() => state.earthShow, val => { if (viewer?.scene?.globe) viewer.scene.globe.show = val; });
-    watch(() => state.shadows, val => { if (viewer?.scene) viewer.scene.shadows = val; });
-    watch(() => state.sunShow, val => { if (viewer?.scene?.globe) viewer.scene.globe.enableLighting = val; });
+    // ==============================================
+    // 基础监听
+    // ==============================================
+    watch(() => state.earthShow, val => { viewer.scene.globe.show = val; });
+    watch(() => state.shadows, val => { viewer.scene.shadows = val; });
+    watch(() => state.sunShow, val => { viewer.scene.globe.enableLighting = val; });
+
+    // ==============================================
+    // 自定义时间轴
+    // ==============================================
     watch(() => state.timeline, val => {
-        const el = document.querySelector(".cesium-viewer-timelineContainer");
-        if (el) el.style.visibility = val ? "visible" : "hidden";
+        // 隐藏原始Cesium时间轴和动画控件
+        nextTick(() => {
+            setTimeout(() => {
+                const t = document.querySelector(".cesium-viewer-timelineContainer");
+                const a = document.querySelector(".cesium-viewer-animationContainer");
+                const b = document.querySelector(".cesium-viewer-bottom");
+                if (t) {
+                    t.style.display = "none";
+                }
+                if (a) {
+                    a.style.display = "none";
+                }
+                if (b) b.style.display = "none";
+            }, 100);
+        });
+    }, { immediate: true });
+
+    watch(() => state.depthAgainst, val => { viewer.scene.globe.depthTestAgainstTerrain = val; });
+    watch(() => state.atomsphereRender, val => { viewer.scene.skyAtmosphere.show = val; });
+    watch(() => state.fogEffect, val => { viewer.scene.fog.enabled = val; });
+    watch(() => state.surfaceTransparency, val => { viewer.scene.globe.globeAlpha = parseFloat(val); });
+    watch(() => state.underground, val => { viewer.scene.undergroundMode = val; });
+
+    // ==============================================
+    // ✅ 帧率正常
+    // ==============================================
+    watch(() => state.frameRate, val => {
+        setTimeout(() => {
+            if (viewer && viewer.scene) viewer.scene.debugShowFramesPerSecond = val;
+        }, 50);
+    });
+
+    watch(() => state.cloud, val => { viewer.scene.cloudBox = val ? cloudBox : null; });
+    watch(() => state.cloudsUrl, val => { viewer.scene.cloudBox.url = val; });
+
+    watch(() => state.skyBox, val => {
+        if (val) {
+            viewer.scene.postRender.addEventListener(watchCameraHeight);
+            viewer.scene.skyBox = skyboxs[state.skyboxType];
+            watchCameraHeight();
+        } else {
+            state.atomsphereRender = true;
+            viewer.scene.skyBox = defaultSkybox;
+            viewer.scene.postRender.removeEventListener(watchCameraHeight);
+        }
     });
-    watch(() => state.depthAgainst, val => { if (viewer?.scene?.globe) viewer.scene.globe.depthTestAgainstTerrain = val; });
-    watch(() => state.atomsphereRender, val => { if (viewer?.scene) viewer.scene.skyAtmosphere.show = val; });
-    watch(() => state.fogEffect, val => { if (viewer?.scene?.fog) viewer.scene.fog.enabled = val; });
-    watch(() => state.surfaceTransparency, val => { if (viewer?.scene?.globe) viewer.scene.globe.alpha = val; });
-    watch(() => state.underground, val => { if (viewer?.scene) viewer.scene.undergroundMode = val; });
-    watch(() => state.frameRate, val => { if (viewer?.scene) viewer.scene.debugShowFramesPerSecond = val; });
-
-    watch(() => state.cloud, val => {
-        if (!viewer || !viewer.scene) return;
-        if (!cloudBox) cloudBox = new Cesium.CloudBox({ url: state.cloudsUrl });
-        viewer.scene.cloudBox = val ? cloudBox : null;
+
+    watch(() => state.skyboxType, val => {
+        if (state.skyBox) {
+            viewer.scene.skyBox = skyboxs[val];
+            skyboxs[val].show = true;
+        }
+    });
+
+    watch(() => state.uspeed, val => {
+        skyboxs[state.skyboxType].USpeed = Number(val);
+        if (state.skyBox) viewer.scene.skyBox.USpeed = Number(val);
+    });
+    watch(() => state.vspeed, val => {
+        skyboxs[state.skyboxType].VSpeed = Number(val);
+        if (state.skyBox) viewer.scene.skyBox.VSpeed = Number(val);
+    });
+    watch(() => state.wspeed, val => {
+        skyboxs[state.skyboxType].WSpeed = Number(val);
+        if (state.skyBox) viewer.scene.skyBox.WSpeed = Number(val);
     });
 
     watch(() => state.viewMode, val => {
-        if (!viewer || !viewer.scene) return;
         if (val === "2D") viewer.scene.mode = Cesium.SceneMode.SCENE2D;
         else if (val === "3D") viewer.scene.mode = Cesium.SceneMode.SCENE3D;
         else viewer.scene.mode = Cesium.SceneMode.COLUMBUS_VIEW;
     });
 
-    watch(() => state.rain, val => { if (viewer?.scene?.postProcessStages?.rain) viewer.scene.postProcessStages.rain.enabled = val; });
-    watch(() => state.snow, val => { if (viewer?.scene?.postProcessStages?.snow) viewer.scene.postProcessStages.snow.enabled = val; });
-    watch(() => state.rainAngle, val => { if (viewer?.scene?.postProcessStages?.rain) viewer.scene.postProcessStages.rain.uniforms.angle = val; });
-    watch(() => state.rainSpeed, val => { if (viewer?.scene?.postProcessStages?.rain) viewer.scene.postProcessStages.rain.uniforms.speed = val; });
-    watch(() => state.snowDesity, val => { if (viewer?.scene?.postProcessStages?.snow) viewer.scene.postProcessStages.snow.uniforms.density = val; });
-    watch(() => state.snowSpeed, val => { if (viewer?.scene?.postProcessStages?.snow) viewer.scene.postProcessStages.snow.uniforms.speed = val; });
-    watch(() => state.snowAngle, val => { if (viewer?.scene?.postProcessStages?.snow) viewer.scene.postProcessStages.snow.uniforms.angle = val; });
+    watch(() => state.rain, val => { viewer.scene.postProcessStages.rain.enabled = val; });
+    watch(() => state.snow, val => { viewer.scene.postProcessStages.snow.enabled = val; });
+    watch(() => state.rainAngle, val => { viewer.scene.postProcessStages.rain.uniforms.angle = parseFloat(val); });
+    watch(() => state.rainSpeed, val => { viewer.scene.postProcessStages.rain.uniforms.speed = parseFloat(val); });
+    watch(() => state.snowDesity, val => { viewer.scene.postProcessStages.snow.uniforms.density = parseFloat(val); });
+    watch(() => state.snowSpeed, val => { viewer.scene.postProcessStages.snow.uniforms.speed = parseFloat(val); });
+    watch(() => state.snowAngle, val => { viewer.scene.postProcessStages.snow.uniforms.angle = parseFloat(val); });
 
     watch(() => state.snowType, val => {
         switch (val) {
@@ -275,9 +333,11 @@ function s3mlayerAttribute(props) {
         }
     });
 
-    watch(() => state.isCompass, val => { state.compass = val ? 'Sm3dCompass' : ''; });
+    watch(() => state.isCompass, val => {
+        state.compass = val ? 'Sm3dCompass' : '';
+    });
 
-    return { ...toRefs(state) };
+    return { ...toRefs(state), handleTimeChange };
 };
 
-export default s3mlayerAttribute
+export default s3mlayerAttribute;

+ 13 - 144
RuoYi-Vue3/src/supermap-cesium-module/components/scene/scene-attribute/scene-attribute.vue

@@ -22,10 +22,6 @@
           <input type="checkbox" v-model="fogEffect" />
           {{Resource.fogEffect}}
         </label>
-        <label style="width:33%">
-          <input type="checkbox" v-model="depthAgainst" />
-          {{Resource.depthAgainst}}
-        </label>
         <label style="width:28%">
           <input type="checkbox" v-model="shadows" />
           {{Resource.shadowMap}}
@@ -34,10 +30,6 @@
           <input type="checkbox" v-model="underground" />
           {{Resource.underground}}
         </label>
-        <label style="width:33%">
-          <input type="checkbox" v-model="frameRate" />
-          {{Resource.frameRate}}
-        </label>
         <label style="width:28%">
           <input type="checkbox" v-model="cloud" />
           {{Resource.cloud}}
@@ -92,143 +84,20 @@
       </div>
     </div>
   </div>
+  <CustomTimeline :visible="timeline" :currentTime="currentTime" @timeChange="handleTimeChange" />
   <component :is="compass"></component>
 </template>
 
 <script>
 import sceneAttribute from "./scene-attribute.js";
+import CustomTimeline from "../custom-timeline/custom-timeline.vue";
 export default {
   name: "Sm3dSceneAttribute",
-  inheritAttrs: false,
-  props: {
-    //地球显影
-    earthShow: {
-      type: Boolean,
-      default: true
-    },
-    //场景阴影
-    shadows: {
-      type: Boolean,
-      default: false
-    },
-    //云层
-    cloud: {
-      type: Boolean,
-      default: false
-    },
-    //天空盒
-    skyBox: {
-      type: Boolean,
-      default: false
-    },
-    //太阳
-    sunShow: {
-      type: Boolean,
-      default: false
-    },
-    //时间轴
-    timeline: {
-      type: Boolean,
-      default: false
-    },
-    //深度检测
-    depthAgainst: {
-      type: Boolean,
-      default: true
-    },
-    //大气渲染
-    atomsphereRender: {
-      type: Boolean,
-      default: true
-    },
-    //雾化效果
-    fogEffect: {
-      type: Boolean,
-      default: true
-    },
-    //开启地下
-    underground: {
-      type: Boolean,
-      default: true
-    },
-    //帧率
-    frameRate: {
-      type: Boolean,
-      default: true
-    },
-    //天空盒自定义对象
-    skyboxSources: {
-      type: Object
-    },
-    //地表透明度
-    surfaceTransparency: {
-      type: Number
-    },
-    //云层纹理路径
-    cloudsUrl: {
-      type: String
-    },
-
-    //天空盒类型
-    skyboxType: {
-      type: String
-    },
-
-    //天空盒子绕x轴运动的动画速度
-    uspeed: {
-      type: Number
-    },
-    //天空盒子绕y轴运动
-    vspeed: {
-      type: Number
-    },
-    //天空盒子绕z轴运动
-    wspeed: {
-      type: Number
-    },
-    //视图模式
-    viewMode: {
-      type: String,
-      default: '3D'
-    },
-    //下雨角度
-    rainAngle: {
-      type: Number
-    },
-    //下雨速度
-    rainSpeed: {
-      type: Number
-    },
-    //下雪密度
-    snowDesity: {
-      type: Number
-    },
-    //下雪速度
-    snowSpeed: {
-      type: Number
-    },
-    //下雪角度
-    snowAngle: {
-      type: Number
-    },
-    //下雪类型
-    snowType: {
-      type: String
-    },
-    //罗盘
-    isCompass: {
-      type: Boolean
-    },
-    //下雨
-    rain: {
-      type: Boolean
-    },
-    //下雪
-    snow: {
-      type: Boolean
-    }
+  components: {
+    CustomTimeline
   },
-  setup(props) {
+  inheritAttrs: false,
+  setup() {
     let {
       earthShow,
       shadows,
@@ -236,19 +105,19 @@ export default {
       skyBox,
       sunShow,
       timeline,
-      depthAgainst,
       atomsphereRender,
       fogEffect,
       underground,
-      frameRate,
       surfaceTransparency,
       viewMode,
       rain,
       snow,
       snowType,
       isCompass,
-      compass
-    } = sceneAttribute(props);
+      compass,
+      currentTime,
+      handleTimeChange
+    } = sceneAttribute();
     return {
       earthShow,
       shadows,
@@ -256,18 +125,18 @@ export default {
       skyBox,
       sunShow,
       timeline,
-      depthAgainst,
       atomsphereRender,
       fogEffect,
       underground,
-      frameRate,
       surfaceTransparency,
       viewMode,
       rain,
       snow,
       snowType,
       isCompass,
-      compass
+      compass,
+      currentTime,
+      handleTimeChange
     };
   }
 };