Pārlūkot izejas kodu

消防应急模拟完成

DESKTOP-6LTVLN7\Liumouren 1 nedēļu atpakaļ
vecāks
revīzija
88b28ac4dd
1 mainītis faili ar 512 papildinājumiem un 206 dzēšanām
  1. 512 206
      src/views/Emergency.vue

+ 512 - 206
src/views/Emergency.vue

@@ -98,6 +98,7 @@ export default {
       waterParticleSystem: null,
       pathPositions: [],
       simulationStep: 0,
+      flameIntensity: 1.0, // 火焰强度,初始为1
     };
   },
   mounted() {
@@ -198,7 +199,8 @@ export default {
             polyline: {
               positions: pathPoints,
               width: 5,
-              material: SkyScenery.Color.RED.withAlpha(0.7),
+              // 修改路径颜色为蓝色系,降低透明度
+              material: SkyScenery.Color.fromCssColorString("#409EFF").withAlpha(0.6),
               clampToGround: true,
             },
           });
@@ -221,6 +223,7 @@ export default {
           this.showPathModal = false;
         });
     },
+
     // 清除路径规划
     clearPath() {
       if (this.pathEntity) {
@@ -247,7 +250,7 @@ export default {
       // 加载消防车模型
       this.loadVehicleModel()
         .then(() => this.createFlameEffect())
-        // .then(() => this.animateVehicleAlongPath())
+        .then(() => this.animateVehicleAlongPath())
         .catch((error) => {
           console.error("模拟启动失败:", error);
           this.showToast("模拟启动失败,请重试", { type: "error" });
@@ -269,11 +272,22 @@ export default {
           return;
         }
         const glbUrl = "/static/gltf/消防车.gltf";
-        if (this.vehicleModel) {
-          this.vehicleModel.remove();
+        if (this.vehicleModel && viewer && viewer.entities) {
+          viewer.entities.remove(this.vehicleModel);
+          this.vehicleModel = null; // 清空引用
         }
         const position = this.pathPositions[0];
-        const heading = SkyScenery.Math.toRadians(135); // 航向角(绕Y轴旋转角度): 135°
+        // 转换为方向角
+        const direction = SkyScenery.Cartesian3.subtract(
+          this.pathPositions[1],
+          this.pathPositions[0],
+          new SkyScenery.Cartesian3()
+        );
+        // console.log(
+        //   Math.atan2(direction.y, direction.x),
+        //   SkyScenery.Math.toRadians(direction)
+        // );
+        const heading = Math.atan2(direction.y, direction.x); // 航向角(绕Y轴旋转角度)
         const pitch = 0; // 俯仰角(绕X轴旋转角度): 0°
         const roll = 0; // 翻滚角(绕Z轴旋转角度): 0°
         const hpr = new SkyScenery.HeadingPitchRoll(heading, pitch, roll);
@@ -298,140 +312,316 @@ export default {
         resolve();
       });
     },
+
     // 创建火焰粒子效果
     createFlameEffect() {
       return new Promise((resolve, reject) => {
-        const destinationPosition = SkyScenery.Cartesian3.fromDegrees(
-          this.endPoint.longitude, // 经度
-          this.endPoint.latitude, // 纬度
-          50 // 高度(米)
-        );
-        if (!destinationPosition) {
-          reject(new Error("终点位置不存在"));
-          return;
-        }
-        // 定义火焰粒子系统的选项
-        const particleSystemOptions = {
-          // 粒子发射器:圆形区域发射
-          emitter: new SkyScenery.CircleEmitter(100.0), // 发射半径
-          image: "/static/image/point.png", // 添加此行(需提供实际纹理图片路径)
-          // 粒子生命周期
-          life: 10.0, // 粒子存活时间(秒)
-          lifeVariance: 0.5, // 生命周期变化范围
-          // 粒子速度
-          speed: 3.0, // 基础速度
-          speedVariance: 1.0, // 速度变化范围
-          // 粒子大小
-
-          startScale: 1,
-          endScale: 5,
-
-          minimumParticleLife: 0.5,
-          maximumParticleLife: 0.5,
-
-          minimumSpeed: 1,
-          maximumSpeed: 4,
-
-          imageSize: new Cesium.Cartesian2(25, 25),
-
-          emissionRate: viewModel.emissionRate,
-
-          // 粒子方向:向上发射为主
-          direction: new SkyScenery.Cartesian3(0.0, 0.0, 1.0), // Z轴向上
-          spread: SkyScenery.Math.toRadians(30.0), // 扩散角度
-
-          // 粒子颜色:火焰颜色渐变(红->橙->黄->透明)
-          startColor: new SkyScenery.Color(1.0, 0.3, 0.0, 1.0), // 初始颜色(红色)
-          endColor: new SkyScenery.Color(1.0, 0.8, 0.0, 1.0), // 结束颜色(透明黄色)
-          // 粒子数量
-          particleCount: 500, // 最大粒子数
-
-          // 发射率:每秒发射的粒子数
-          emissionRate: 100,
-
-          // 粒子更新函数:模拟火焰的随机飘动
-          updateCallback: function (particle, dt) {
-            // 添加随机横向运动,模拟火焰飘动
-            const wind = 0.5; // 风力系数
-            particle.velocity.x += (Math.random() - 0.5) * wind * dt;
-            particle.velocity.y += (Math.random() - 0.5) * wind * dt;
-
-            // 随时间增加粒子透明度衰减
-            particle.color.alpha = SkyScenery.Math.lerp(
-              particle.startColor.alpha,
-              particle.endColor.alpha,
-              particle.life / particle.startLife
+        try {
+          let endPoint = this.endPoint;
+          const vm = this;
+          // 创建Canvas元素
+          const canvas = document.createElement("canvas");
+          canvas.id = "fireCanvas";
+          canvas.style.position = "absolute";
+          canvas.style.pointerEvents = "none"; // 让鼠标事件穿透canvas
+          document.body.appendChild(canvas);
+
+          // 设置Canvas尺寸,适当增大以避免溢出
+          canvas.width = 80;
+          canvas.height = 160;
+
+          const ctx = canvas.getContext("2d");
+
+          // 火焰粒子数组
+          let particles = [];
+          // 火焰强度控制
+          let intensity = 100;
+
+          // 添加火焰强度衰减控制
+          this.flameIntensity = 1.0;
+
+          // 粒子类 - 优化火焰效果
+          class Particle {
+            constructor(x, y) {
+              this.x = x;
+              this.y = y;
+              // 减小粒子大小,从原来的5-20改为2-8
+              this.size = Math.random() * 6 + 2;
+              // 调整X方向速度范围,减少水平扩散
+              this.speedX = Math.random() * 3 - 1.5;
+              // 调整Y方向速度,使火焰更向上集中
+              this.speedY = Math.random() * -2 - 2;
+              // 优化火焰颜色,使用更真实的火焰色调范围
+              const hue = Math.random() * 30 + 15; // 色调范围:15-45
+              const lightness = Math.random() * 20 + 40; // 亮度范围:40-60
+              this.color = `hsl(${hue}, 100%, ${lightness}%)`;
+              this.alpha = Math.random() * 0.5 + 0.5; // 透明度范围:0.5-1.0
+              this.decay = Math.random() * 0.01 + 0.005; // 透明度衰减速度
+              this.originalSize = this.size; // 保存初始大小用于动画
+            }
+
+            update() {
+              // 应用火焰强度衰减
+              const effectiveSize = this.size * this.originalSize * vm.flameIntensity;
+
+              // 向上运动时逐渐减速
+              this.speedY += 0.05; // 重力效果,使粒子上升变慢
+              this.x += this.speedX * (1 - this.y / canvas.height); // 上部水平速度减慢
+              this.y += this.speedY;
+
+              // 透明度衰减
+              this.alpha -= this.decay * (1 - this.y / canvas.height);
+
+              // 大小变化:先增大后减小,更像火焰
+              const lifeProgress = 1 - this.alpha;
+              this.size =
+                this.originalSize *
+                vm.flameIntensity *
+                (lifeProgress < 0.3
+                  ? 1 + lifeProgress * 2
+                  : 1 - (lifeProgress - 0.3) * 3);
+
+              // 添加更自然的随机性,上部随机性减小
+              const randomFactor = 1 - this.y / canvas.height;
+              this.speedX += (Math.random() * 0.6 - 0.3) * randomFactor;
+            }
+
+            draw() {
+              ctx.globalAlpha = this.alpha;
+              ctx.fillStyle = this.color;
+              ctx.beginPath();
+              // 使用椭圆代替圆形,使火焰更扁平
+              ctx.ellipse(this.x, this.y, this.size * 0.8, this.size, 0, 0, Math.PI * 2);
+              ctx.fill();
+            }
+
+            isDead() {
+              return this.alpha <= 0 || this.size <= 0.5;
+            }
+          }
+
+          // 创建粒子 - 增加粒子数量以弥补大小减小
+          function createParticles() {
+            const particleCount = (intensity / 100) * 10; // 从5增加到10
+            // 从底部中心区域发射粒子,更集中
+            for (let i = 0; i < particleCount; i++) {
+              const x =
+                canvas.width / 2 + (Math.random() * 30 - 15) * (1 - Math.random() * 0.5);
+              const y = canvas.height - 10;
+              particles.push(new Particle(x, y));
+            }
+          }
+
+          // 处理粒子
+          function handleParticles() {
+            for (let i = 0; i < particles.length; i++) {
+              particles[i].update();
+              particles[i].draw();
+              if (particles[i].isDead()) {
+                particles.splice(i, 1);
+                i--;
+              }
+            }
+          }
+
+          // 绘制火焰底部 - 更集中的火焰源头
+          function drawFireBase() {
+            const gradient = ctx.createRadialGradient(
+              canvas.width / 2,
+              canvas.height - 10,
+              5,
+              canvas.width / 2,
+              canvas.height - 10,
+              40
             );
-          },
+            gradient.addColorStop(0, `rgba(255, 100, 0, ${0.6 * vm.flameIntensity})`);
+            gradient.addColorStop(1, `rgba(255, 50, 0, 0)`);
+
+            ctx.globalAlpha = 0.6;
+            ctx.fillStyle = gradient;
+            ctx.beginPath();
+            ctx.arc(canvas.width / 2, canvas.height - 10, 40, 0, Math.PI * 2);
+            ctx.fill();
+          }
 
-          // 粒子位置
-          modelMatrix: SkyScenery.Transforms.eastNorthUpToFixedFrame(destinationPosition),
-        };
+          // 更新火焰位置到地图终点
+          function updateFlamePosition() {
+            if (!endPoint || !viewer) return;
 
-        // 创建粒子系统
-        const particleSystem = new SkyScenery.ParticleSystem(particleSystemOptions);
+            // 将地理坐标转换为屏幕坐标
+            const destinationCartesian = SkyScenery.Cartesian3.fromDegrees(
+              parseFloat(endPoint.longitude),
+              parseFloat(endPoint.latitude),
+              0 // 高度偏移,让火焰悬浮在地面上方
+            );
 
-        // 验证粒子系统是否创建成功
-        if (!particleSystem) {
-          reject(new Error("火焰粒子系统创建失败"));
-          return;
-        } else {
-          // 添加到场景
-          console.log("火焰粒子系统创建成功", particleSystem);
-          this.flameParticleSystem = viewer.scene.primitives.add(particleSystem);
-          // viewer.imageryLayers.removeAll(); // 移除影像图层
-          // 确保相机飞到粒子位置
-          viewer.camera.flyTo({
-            destination: SkyScenery.Cartesian3.fromDegrees(
-              this.endPoint.longitude, // 经度
-              this.endPoint.latitude, // 纬度
-              500 // 高度(米)
-            ),
-            duration: 2,
-          });
+            const windowPosition = SkyScenery.SceneTransforms.wgs84ToWindowCoordinates(
+              viewer.scene,
+              destinationCartesian
+            );
+
+            if (windowPosition) {
+              // 设置canvas位置,使其中心点对准终点
+              canvas.style.left = `${windowPosition.x - canvas.width / 2}px`;
+              canvas.style.top = `${windowPosition.y - canvas.height * 0.8}px`;
+            }
+          }
+
+          // 动画循环
+          function animate() {
+            // 清除画布
+            ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+            // 更新火焰位置
+            updateFlamePosition();
+
+            drawFireBase();
+            createParticles();
+            handleParticles();
+
+            requestAnimationFrame(animate);
+          }
+
+          // 初始化
+          function init() {
+            // 监听地图渲染事件,更新火焰位置
+            viewer.scene.postRender.addEventListener(updateFlamePosition);
+
+            // 开始动画
+            animate();
+            resolve(canvas); // 初始化完成,返回canvas元素
+          }
+
+          // 启动初始化
+          init();
+        } catch (error) {
+          reject(error);
         }
-        resolve();
       });
-    },
-    // 车辆沿路径动画
+    }, // 车辆沿路径动画
     animateVehicleAlongPath() {
       const totalDuration = 10000; // 10秒完成路径
-      const stepDuration = totalDuration / this.pathPositions.length;
-      let currentStep = 0;
+      const startTime = performance.now();
+      const pathLength = this.pathPositions.length;
+      // 计算路径总长度用于速度控制
+      let totalPathDistance = 0;
+      for (let i = 0; i < pathLength - 1; i++) {
+        totalPathDistance += SkyScenery.Cartesian3.distance(
+          this.pathPositions[i],
+          this.pathPositions[i + 1]
+        );
+      }
+
+      let animateStep = (currentTime) => {
+        const elapsed = currentTime - startTime;
+        const progress = Math.min(elapsed / totalDuration, 1.0);
+
+        // 根据进度计算当前位置
+        const targetDistance = progress * totalPathDistance;
+        let accumulatedDistance = 0;
+        // 找到当前所在路径段
+        for (let i = 0; i < pathLength - 1; i++) {
+          const segmentDistance = SkyScenery.Cartesian3.distance(
+            this.pathPositions[i],
+            this.pathPositions[i + 1]
+          );
+
+          if (accumulatedDistance + segmentDistance >= targetDistance) {
+            const segmentProgress =
+              (targetDistance - accumulatedDistance) / segmentDistance;
+            // 线性插值计算当前位置
+            this.vehicleModel.position = SkyScenery.Cartesian3.lerp(
+              this.pathPositions[i],
+              this.pathPositions[i + 1],
+              segmentProgress,
+              new SkyScenery.Cartesian3()
+            );
 
-      const animateStep = () => {
-        if (currentStep < this.pathPositions.length) {
-          this.vehicleModel.position = this.pathPositions[currentStep];
-          currentStep++;
+            // 计算方向向量并转向
+            if (i < pathLength - 1) {
+              const direction = SkyScenery.Cartesian3.subtract(
+                this.pathPositions[i + 1],
+                this.pathPositions[i],
+                new SkyScenery.Cartesian3()
+              );
+              // 转换为方向角
+              const heading = Math.atan2(direction.y, direction.x);
+              const position = this.vehicleModel.position;
+              const orientation = SkyScenery.Transforms.headingPitchRollQuaternion(
+                position,
+                new SkyScenery.HeadingPitchRoll(heading, 0, 0)
+              );
+            }
+            break;
+          }
+          accumulatedDistance += segmentDistance;
+        }
+
+        if (progress < 1.0) {
           requestAnimationFrame(animateStep);
         } else {
-          // 车辆到达终点,开始灭火作业
+          // 消防车到达终点前的位置偏移(距离终点5米)
+          if (pathLength >= 2) {
+            // 获取最后一段路径向量
+            const lastSegment = SkyScenery.Cartesian3.subtract(
+              this.pathPositions[pathLength - 1],
+              this.pathPositions[pathLength - 2],
+              new SkyScenery.Cartesian3()
+            );
+            // 标准化向量
+            SkyScenery.Cartesian3.normalize(lastSegment, lastSegment);
+            // 反向移动5米(根据实际场景调整距离)
+            SkyScenery.Cartesian3.multiplyByScalar(lastSegment, -5, lastSegment);
+
+            // 设置最终位置(终点前5米)
+            this.vehicleModel.position = SkyScenery.Cartesian3.add(
+              this.pathPositions[pathLength - 1],
+              lastSegment,
+              new SkyScenery.Cartesian3()
+            );
+          } else {
+            // 路径点不足时直接使用终点
+            this.vehicleModel.position = this.pathPositions[pathLength - 1];
+          }
           this.startExtinguishing();
         }
       };
-
-      animateStep();
+      requestAnimationFrame(animateStep);
     },
     // 开始灭火作业
     startExtinguishing() {
       this.showToast("消防车到达目的地,开始灭火作业...");
 
-      // 创建水流粒子效果
-      this.createWaterEffect();
+      // 创建水流粒子效果(添加Promise链式调用)
+      this.createWaterEffect().catch((error) => {
+        console.error("水流效果创建失败:", error);
+      });
 
       // 3秒后开始减少火焰
       setTimeout(() => {
-        if (this.flameParticleSystem) {
-          this.flameParticleSystem.emissionRate = 10; // 减少粒子发射量
-        }
+        // 火焰强度逐渐减弱
+        let interval = setInterval(() => {
+          if (this.flameIntensity > 0) {
+            this.flameIntensity -= 0.02;
+          } else {
+            clearInterval(interval);
+          }
+        }, 50);
       }, 3000);
 
       // 5秒后完全移除火焰
       setTimeout(() => {
-        if (this.flameParticleSystem) {
-          viewer.scene.primitives.remove(this.flameParticleSystem);
-          this.flameParticleSystem = null;
-        }
+        const canvas = document.getElementById("fireCanvas");
+        if (canvas) canvas.remove();
+
+        // 显示完成弹窗
+        this.showToast("作业完成,起火点已熄灭!");
+
+        // 1秒后显示模拟完成
+        setTimeout(() => {
+          this.showToast("模拟完成");
+          this.isSimulating = false;
+        }, 1000);
       }, 5000);
 
       // 6秒后完成作业
@@ -441,74 +631,219 @@ export default {
     },
     // 创建水流粒子效果
     createWaterEffect() {
-      try {
-        const vehiclePosition = this.vehicleModel.position;
-        const destinationPosition = this.pathPositions[this.pathPositions.length - 1];
+      return new Promise((resolve, reject) => {
+        try {
+          const vm = this;
+          const endPoint = this.endPoint;
+
+          // 创建Canvas元素
+          const canvas = document.createElement("canvas");
+          canvas.id = "waterCanvas";
+          canvas.style.position = "absolute";
+          canvas.style.pointerEvents = "none"; // 让鼠标事件穿透canvas
+          document.body.appendChild(canvas);
+
+          // 设置Canvas尺寸
+          canvas.width = 120; // 较宽的画布以覆盖水流范围
+          canvas.height = 120;
+
+          const ctx = canvas.getContext("2d");
+
+          // 水流粒子数组
+          let particles = [];
+          // 水流强度控制
+          let waterIntensity = 1.0;
+
+          // 粒子类 - 水流效果
+          class WaterParticle {
+            constructor(x, y, targetX, targetY) {
+              this.x = x;
+              this.y = y;
+              this.targetX = targetX; // 目标X坐标(火焰位置)
+              this.targetY = targetY; // 目标Y坐标(火焰位置)
+              this.size = Math.random() * 4 + 2; // 水滴大小(2-6)
+
+              // 计算指向火焰的初始速度
+              const angle = Math.atan2(targetY - y, targetX - x);
+              const speed = Math.random() * 4 + 6; // 速度(6-10)
+              this.speedX = Math.cos(angle) * speed;
+              this.speedY = Math.sin(angle) * speed;
+
+              // 水滴颜色(蓝色系)
+              const hue = Math.random() * 20 + 180; // 色调范围:180-200(蓝色)
+              const lightness = Math.random() * 30 + 60; // 亮度范围:60-90
+              this.color = `hsl(${hue}, 80%, ${lightness}%)`;
+
+              this.alpha = Math.random() * 0.5 + 0.5; // 透明度(0.5-1.0)
+              this.decay = Math.random() * 0.015 + 0.005; // 衰减速度
+              this.gravity = 0.15; // 重力影响
+            }
 
-        if (!vehiclePosition || !destinationPosition) {
-          throw new Error("车辆或终点位置未定义");
-        }
+            update() {
+              // 应用重力
+              this.speedY += this.gravity;
 
-        this.waterParticleSystem = viewer.scene.primitives.add(
-          new SkyScenery.ParticleSystem({
-            image: require("@static/image/point.png"), // 使用本地水资源图片
-            startColor: new SkyScenery.Color(0.1, 0.5, 1.0, 0.8),
-            endColor: new SkyScenery.Color(0.1, 0.5, 1.0, 0.0),
-            startScale: 5.0,
-            endScale: 15.0,
-            particleLife: 1.0,
-            emissionRate: 30.0,
-            speed: 10.0,
-            width: 10,
-            height: 10,
-            lifeTime: 3.0,
-            position: vehiclePosition,
-            emitter: new SkyScenery.CircleEmitter(2.0),
-            velocity: SkyScenery.Cartesian3.subtract(
-              destinationPosition,
-              vehiclePosition,
-              new SkyScenery.Cartesian3()
-            ),
-          })
-        );
-      } catch (error) {
-        console.error("水流粒子效果创建失败:", error);
-        this.showToast("水流效果加载失败");
-      }
+              // 更新位置
+              this.x += this.speedX * waterIntensity;
+              this.y += this.speedY * waterIntensity;
+
+              // 透明度衰减
+              this.alpha -= this.decay;
+
+              // 水滴下落时轻微摇摆
+              this.speedX += (Math.random() - 0.5) * 0.3;
+            }
+
+            draw() {
+              ctx.globalAlpha = this.alpha;
+              ctx.fillStyle = this.color;
+              ctx.beginPath();
+              // 确保半径为正数
+              const radiusX = Math.max(0.1, this.size * 0.8);
+              const radiusY = Math.max(0.1, this.size);
+              // 水滴形状(略微扁圆)
+              ctx.ellipse(this.x, this.y, radiusX, radiusY, 0, 0, Math.PI * 2);
+              ctx.fill();
+            }
+
+            isDead() {
+              return this.alpha <= 0 || this.size <= 0.5;
+            }
+          }
+
+          // 创建水流粒子
+          function createWaterParticles(truckX, truckY, flameX, flameY) {
+            const particleCount = 8; // 每次发射粒子数量
+            for (let i = 0; i < particleCount; i++) {
+              // 从消防车位置附近随机发射
+              const offsetX = (Math.random() - 0.5) * 15;
+              const offsetY = (Math.random() - 0.5) * 10;
+              particles.push(
+                new WaterParticle(
+                  truckX + offsetX,
+                  truckY + offsetY,
+                  flameX, // 目标火焰X
+                  flameY // 目标火焰Y
+                )
+              );
+            }
+          }
+
+          // 处理水流粒子
+          function handleWaterParticles() {
+            for (let i = 0; i < particles.length; i++) {
+              particles[i].update();
+              particles[i].draw();
+              if (particles[i].isDead()) {
+                particles.splice(i, 1);
+                i--;
+              }
+            }
+          }
+
+          // 更新水流位置(消防车 -> 火焰)
+          function updateWaterPosition() {
+            if (!vm.vehicleModel || !endPoint || !viewer) return;
+
+            // 获取消防车屏幕坐标 - 修复位置克隆问题
+            const currentTime = viewer.clock.currentTime;
+            // 获取当前位置的Cartesian3值再克隆
+            const truckPosition = vm.vehicleModel.position.getValue(currentTime);
+            const truckCartesian = truckPosition.clone(); // 现在可以安全调用clone()
+
+            const truckWindowPos = SkyScenery.SceneTransforms.wgs84ToWindowCoordinates(
+              viewer.scene,
+              truckCartesian
+            );
+
+            // 获取火焰屏幕坐标
+            const flameCartesian = SkyScenery.Cartesian3.fromDegrees(
+              parseFloat(endPoint.longitude),
+              parseFloat(endPoint.latitude),
+              0
+            );
+            const flameWindowPos = SkyScenery.SceneTransforms.wgs84ToWindowCoordinates(
+              viewer.scene,
+              flameCartesian
+            );
+
+            if (truckWindowPos && flameWindowPos) {
+              // 设置Canvas位置(消防车位置)
+              canvas.style.left = `${truckWindowPos.x - canvas.width / 2}px`;
+              canvas.style.top = `${truckWindowPos.y - canvas.height / 2}px`;
+
+              // 创建水流粒子(从消防车到火焰)
+              createWaterParticles(
+                canvas.width / 2, // 消防车在Canvas内的X
+                canvas.height / 2, // 消防车在Canvas内的Y
+                flameWindowPos.x - truckWindowPos.x + canvas.width / 2, // 火焰相对X
+                flameWindowPos.y - truckWindowPos.y + canvas.height / 2 // 火焰相对Y
+              );
+            }
+          }
+
+          // 动画循环
+          function animate() {
+            // // 清除画布(保留轻微拖尾效果)
+            // ctx.fillStyle = "rgba(0, 0, 0, 0.05)";
+            // ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+            // 修复:使用完全透明的背景,而不是半透明黑色
+            ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+            // 更新位置并创建粒子
+            updateWaterPosition();
+
+            // 绘制粒子
+            handleWaterParticles();
+
+            requestAnimationFrame(animate);
+          }
+
+          // 初始化
+          function init() {
+            // 监听地图渲染事件,更新水流位置
+            viewer.scene.postRender.addEventListener(updateWaterPosition);
+
+            // 开始动画
+            animate();
+
+            // 3秒后停止水流
+            setTimeout(() => {
+              waterIntensity = 0;
+              // 等待剩余粒子消失后移除Canvas
+              setTimeout(() => {
+                canvas.remove();
+              }, 1500);
+            }, 3000);
+
+            resolve(canvas);
+          }
+
+          // 启动初始化
+          init();
+        } catch (error) {
+          reject(error);
+        }
+      });
     },
     // 完成模拟
     completeSimulation() {
       // 移除车辆和粒子效果
-      if (this.vehicleModel) {
-        viewer.entities.remove(this.vehicleModel);
-        this.vehicleModel = null;
-      }
-      if (this.waterParticleSystem) {
-        viewer.scene.primitives.remove(this.waterParticleSystem);
-        this.waterParticleSystem = null;
-      }
+      // if (this.vehicleModel) {
+      //   viewer.entities.remove(this.vehicleModel);
+      //   this.vehicleModel = null;
+      // }
 
       // 显示完成弹窗
-      this.showCompletionModal("作业完成,起火点已熄灭!");
+      this.showToast("作业完成,起火点已熄灭!");
 
       // 1秒后显示模拟完成
       setTimeout(() => {
-        this.showCompletionModal("模拟完成");
+        this.showToast("模拟完成");
         this.isSimulating = false;
       }, 1000);
     },
-    // 显示完成弹窗
-    showCompletionModal(message) {
-      const modal = document.createElement("div");
-      modal.className = "simulation-completion-modal";
-      modal.innerHTML = `
-        <div class="modal-content">
-          <h3>${message}</h3>
-          <button class="ok-btn" onclick="document.querySelector('.simulation-completion-modal').remove()">确定</button>
-        </div>
-      `;
-      document.body.appendChild(modal);
-    },
     showToast(message) {
       // 创建自定义提示框
       let toast = document.createElement("div");
@@ -684,7 +1019,10 @@ export default {
       this.removeMapMarker(type);
 
       // 创建新标记
-      const color = type === "start" ? SkyScenery.Color.BLUE : SkyScenery.Color.RED;
+      const color =
+        type === "start"
+          ? SkyScenery.Color.fromCssColorString("#409EFF") // 柔和蓝色
+          : SkyScenery.Color.fromCssColorString("#FF9F40"); // 柔和橙色
       const labelText = type === "start" ? "起点" : type === "end" ? "终点" : "选点";
 
       viewer.entities.add({
@@ -694,17 +1032,20 @@ export default {
         point: {
           show: true,
           color: color,
-          pixelSize: 20,
-          outlineColor: SkyScenery.Color.YELLOW,
-          outlineWidth: 3,
+          pixelSize: 12,
+          outlineColor: SkyScenery.Color.WHITE,
+          outlineWidth: 2,
         },
         label: {
           text: labelText,
-          font: "normal 18px 楷体",
+          font: "bold 16px 楷体", // 增大字号并加粗
           fillColor: color,
           horizontalOrigin: SkyScenery.HorizontalOrigin.CENTER,
           verticalOrigin: SkyScenery.VerticalOrigin.BOTTOM,
-          pixelOffset: new SkyScenery.Cartesian2(0, -20),
+          pixelOffset: new SkyScenery.Cartesian2(0, -15),
+          // 添加文字描边效果
+          outlineColor: SkyScenery.Color.BLACK,
+          outlineWidth: 2,
         },
       });
     },
@@ -774,7 +1115,7 @@ export default {
       viewer.imageryLayers.addImageryProvider(
         new SkyScenery.ArcGisMapServerImageryProvider({
           url:
-            "https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer",
+            "https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer",
         })
       );
 
@@ -993,39 +1334,4 @@ export default {
 .select-btn:hover:not(.active) {
   background-color: #66b1ff;
 }
-/* 模拟完成弹窗样式 */
-.simulation-completion-modal {
-  position: fixed;
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: 100%;
-  background: rgba(0, 0, 0, 0.5);
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  z-index: 2000;
-}
-
-.simulation-completion-modal .modal-content {
-  background: white;
-  padding: 30px;
-  border-radius: 8px;
-  text-align: center;
-  min-width: 300px;
-}
-
-.simulation-completion-modal h3 {
-  color: #1f2d3d;
-  margin-bottom: 20px;
-}
-
-.simulation-completion-modal .ok-btn {
-  background-color: #1890ff;
-  color: white;
-  border: none;
-  padding: 8px 16px;
-  border-radius: 4px;
-  cursor: pointer;
-}
 </style>