最终效果
<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>