使用cesium完成一个简单的多路径小圆点移动动画
<template>
<div class="cesiumModel" id="cesiumModel"></div>
</template>
<script>
// 这里我把路径单独提成1个文件方便维护,实际项目中也可能是后台返回的数据
import { rainPath } from '../utils/path'
export default {
data() {
return {
viewer: null,
paths: [], // 路径数组
entities: [], // 小圆点实体数组
numberOfPoints: 2, // 每条路径的圆点数量
animationFrame: null,
};
},
mounted() {
this.init();
},
methods: {
init() {
this.viewer = new Cesium.Viewer("cesiumModel", {
animation: false,
timeline: false,
sceneModePicker: false,
requestRenderMode: false,
});
// 添加点击事件 方便后期获取位置参数
this.viewer.scene.canvas.addEventListener("click", this.onMapClick);
// 定义路径数据
this.paths = sewagePath
// 初始化实体
this.createMovingPoints();
// 设置视角
this.viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(111.755575, 40.737001, 4000),
});
// 开始动画
this.startAnimation();
},
onMapClick(event) {
const scene = this.viewer.scene;
const camera = this.viewer.camera;
// 获取点击位置的屏幕坐标
const mousePosition = new Cesium.Cartesian2(event.clientX, event.clientY);
// 从屏幕坐标获取地球上的三维坐标
const cartesian = scene.pickPosition(mousePosition);
if (Cesium.defined(cartesian)) {
// 转换为经纬度坐标
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
const height = cartographic.height;
console.log(`经度: ${longitude}, 纬度: ${latitude}, 高度: ${height}`);
// 在地图上添加一个点作为标记
this.viewer.entities.add({
position: cartesian,
point: {
pixelSize: 10,
color: Cesium.Color.RED,
},
});
}
},
// 创建实体
createMovingPoints() {
this.paths.forEach((path, pathIndex) => {
const { points } = path;
const segments = points.length - 1;
const segmentLength = 1 / segments; // 每段路径的插值范围
for (let i = 0; i < path.num; i++) {
// 计算实体在总路径中的位置
const globalFraction = i / path.num;
// 确定当前在哪一段路径上
const segmentIndex = Math.floor(globalFraction / segmentLength);
const segmentFraction = (globalFraction % segmentLength) / segmentLength;
// 确保插值在有效范围内
if (segmentIndex >= segments) continue;
const startPoint = points[segmentIndex];
const endPoint = points[segmentIndex + 1];
// 计算插值位置
const position = Cesium.Cartesian3.lerp(
startPoint,
endPoint,
segmentFraction,
new Cesium.Cartesian3()
);
// 创建实体
const arrow = this.viewer.entities.add({
position: position,
billboard: { // 不一定用billboard,也可以用point等多种实现方式
image: "/static/1.png", //这里填写你的图片地址
width: 20,
height: 20,
rotation: path.rotation
},
});
// 存储实体及路径信息
this.entities.push({ entity: arrow, pathIndex, globalFraction });
}
});
},
// 动画逻辑
startAnimation() {
const animate = () => {
this.paths.forEach((path, pathIndex) => {
const { points, speed } = path;
// 更新偏移量
path.currentOffset += speed;
if (path.currentOffset >= 1.0) {
path.currentOffset = 0.0;
}
// 每条路径更新圆点位置
this.entities.forEach(({ entity, pathIndex: entityPathIndex, globalFraction }) => {
if (pathIndex === entityPathIndex) {
// 计算全局偏移
const offsetFraction = (globalFraction + path.currentOffset) % 1.0;
// 确定所在路径段
const segments = points.length - 1;
const segmentLength = 1 / segments;
const segmentIndex = Math.floor(offsetFraction / segmentLength);
const segmentFraction =
(offsetFraction % segmentLength) / segmentLength;
// 确保插值在有效范围内
if (segmentIndex >= segments) return;
const startPoint = points[segmentIndex];
const endPoint = points[segmentIndex + 1];
// 计算插值位置
const newPosition = Cesium.Cartesian3.lerp(
startPoint,
endPoint,
segmentFraction,
new Cesium.Cartesian3()
);
// 更新实体位置
entity.position = newPosition;
}
});
});
// 请求重新渲染
this.viewer.scene.requestRender();
// 循环动画
this.animationFrame = requestAnimationFrame(animate);
};
animate();
},
},
beforeDestroy() {
cancelAnimationFrame(this.animationFrame);
},
}
</script>
<style>
.cesiumModel {
width: 100%;
height: 100vh;
}
</style>
export const rainPath = [ // 根据自己实际情况添加
{
points: [ // 路径
Cesium.Cartesian3.fromDegrees(-75.0, 39.0),
Cesium.Cartesian3.fromDegrees(-75.1, 39.1),
Cesium.Cartesian3.fromDegrees(-75.2, 39.2),
],
currentOffset: 0, // 偏移量
num: 10, // 数量
speed: 0.01, // 速度
rotation: -300 // 旋转
}
]