Vue3中Cesium实现移动

最终效果

<template>
  <div class="car-control-buttons">
    <button @click="handleStartCarAnimation">启动小车</button>
    <button @click="handlePauseCarAnimation">暂停小车</button>
  </div>
</template>
<script>
import { ref, reactive, onMounted, watch } from "vue";
import * as Cesium from "cesium";

export default {
  name: "CarControlButtons",
  props: {
    viewer: {
      required: true,
    },
  },
  setup(props) {
    onMounted(async () => {});
    let carModel = null; // 用于存储小车模型的引用
    let carPathPositions = []; // 存储线的顶点坐标,用于小车移动的路径
    let isCarMoving = false; // 控制小车是否正在移动
    let isPaused = false; // 控制小车是否处于暂停状态
    let carSpeed = 1000; // 小车的速度,单位为米/秒
    let startTime = null; // 用于记录动画开始的时间
    let pauseTime = null; // 用于记录暂停的时间
    let lastFractionCompleted = 0; // 用于记录暂停时的动画进度
    let handler = null;

    // 加载小车模型
    const loadCarModel = (positions) => {
      carModel = props.viewer.entities.add({
        position: new Cesium.CallbackProperty(() => {
          if (isCarMoving && !isPaused) {
            const currentTime = Cesium.JulianDate.now(); // 获取当前时间
            const timeSinceStart = Cesium.JulianDate.secondsDifference(
              currentTime,
              startTime
            ); // 计算开始以来经过的时间。
            console.log("timeSinceStart", timeSinceStart);

            const distanceTraveled = timeSinceStart * carSpeed; // 计算小车已经行驶的距离
            const totalDistance = getTotalPathDistance(carPathPositions); // 计算整个路径的总长度
            const fractionCompleted =
              (distanceTraveled % totalDistance) / totalDistance; // 计算小车在路径上完成的比例
            return interpolateAlongPath(carPathPositions, fractionCompleted); // 根据完成的比例插值计算小车的当前位置。
          }
          return interpolateAlongPath(carPathPositions, lastFractionCompleted); // 暂停时保持当前位置
        }, false),
        model: {
          uri: "/public/Duck.glb",
          scale: 1.0,
          minimumPixelSize: 128,
          maximumScale: 20000,
        },
      });
      carPathPositions = positions;
      isCarMoving = false; // 重置移动状态
      isPaused = false; // 重置暂停状态
      startTime = null; // 重置动画开始时间
      pauseTime = null; // 重置暂停时间
      lastFractionCompleted = 0; // 重置动画进度
    };

    // 启动小车动画
    const startCarAnimation = () => {
      if (carModel && !isCarMoving) {
        isCarMoving = true;
        isPaused = false;
        startTime = Cesium.JulianDate.now(); // 记录动画开始的时间
        lastFractionCompleted = 0; // 重置动画进度
      } else if (carModel && isCarMoving && isPaused) {
        isPaused = false;
        const currentTime = Cesium.JulianDate.now();
        const timeSincePause = Cesium.JulianDate.secondsDifference(
          currentTime,
          pauseTime
        );
        // 更新开始时间,考虑暂停的时间
        startTime = Cesium.JulianDate.addSeconds(
          startTime,
          timeSincePause,
          new Cesium.JulianDate()
        );
      }
    };

    // 暂停小车动画
    const pauseCarAnimation = () => {
      if (isCarMoving && !isPaused) {
        isPaused = true;
        pauseTime = Cesium.JulianDate.now(); // 记录暂停的时间
        const currentTime = Cesium.JulianDate.now();
        const timeSinceStart = Cesium.JulianDate.secondsDifference(
          currentTime,
          startTime
        );
        const distanceTraveled = timeSinceStart * carSpeed;
        const totalDistance = getTotalPathDistance(carPathPositions);
        lastFractionCompleted =
          (distanceTraveled % totalDistance) / totalDistance; // 保存当前动画进度
      }
    };

    // 计算路径总长度
    const getTotalPathDistance = (positions) => {
      let totalDistance = 0;
      for (let i = 1; i < positions.length; i++) {
        totalDistance += Cesium.Cartesian3.distance(
          positions[i - 1],
          positions[i]
        );
      }
      return totalDistance;
    };

    // 沿路径插值
    const interpolateAlongPath = (positions, fraction) => {
      let accumulatedDistance = 0; // 用于记录累积的距离
      const totalDistance = getTotalPathDistance(positions);
      for (let i = 1; i < positions.length; i++) {
        const segmentDistance = Cesium.Cartesian3.distance(
          positions[i - 1],
          positions[i]
        ); // 计算相邻两个顶点之间的距离

        // 如果累积距离 加上  当前段距离大于等于  总距离乘以    给定的完成比例,执行以下代码块。
        if (accumulatedDistance + segmentDistance >= totalDistance * fraction) {
          // 计算当前段上给定完成比例对应的分段比例,用于插值计算。
          const segmentFraction =
            (totalDistance * fraction - accumulatedDistance) / segmentDistance;
          return Cesium.Cartesian3.lerp(
            positions[i - 1],
            positions[i],
            segmentFraction,
            new Cesium.Cartesian3()
          );
        }
        accumulatedDistance += segmentDistance; // 累积当前段的距离
      }
      return positions[positions.length - 1]; // 如果没有找到合适的插值位置,则返回路径上的最后一个顶点坐标。
    };

    // 双击事件处理
    const handleDoubleClick = (movement) => {
      let pickedObject = props.viewer.scene.pick(movement.position);
      if (
        pickedObject &&
        pickedObject.id instanceof Cesium.Entity &&
        pickedObject.id.type === "polyline"
      ) {
        const polylineEntity = pickedObject.id;
        const positions = polylineEntity.polyline.positions.getValue();
        loadCarModel(positions);
      }
    };

    // 监听 viewer 变化,确保 viewer 已经初始化
    watch(
      () => props.viewer,
      (newViewer) => {
        if (newViewer && newViewer.scene) {
          if (!handler) {
            handler = new Cesium.ScreenSpaceEventHandler(
              newViewer.scene.canvas
            );
          }
          handler.setInputAction(
            handleDoubleClick,
            Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
          );
        }
      },
      { immediate: true }
    );

    return {
      handleStartCarAnimation: startCarAnimation,
      handlePauseCarAnimation: pauseCarAnimation,
    };
  },
};
</script>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值