前言
经过这么久的学习,对Cesium应该有了更深一步的认识,针对前面的学习,现在我们来实现一个小demo,实现让3D模型飞机沿着指定路线飞行。
技术方案
首先初始化地图,然后导入3D飞机模型,确定两点之间的航线,再让飞机沿着航线动起来。这里的动画我们可以采用Cesium用于跟踪模拟时间的简单时钟和时间线来实现,下面就具体还实现一下。
实现demo
一、初始化
首先初始化地图,并配置有关时间Clock 的参数,如:开始时间,结束时间,当前时间,时间线等相关配置。具体可看官网API解释:API传送门
代码:
this.startTime = Cesium.JulianDate.fromDate(new Date('2025-02-18 16:00:00'));
this.stopTime = Cesium.JulianDate.addSeconds(this.startTime, 360, new Cesium.JulianDate());
this.cesiumViewer = new Cesium.Viewer('cesiumContainer', {
infoBox: false,
baseLayerPicjer: false,
// 设置地形实体化
terrainProvider: new Cesium.CesiumTerrainProvider({
url: Cesium.IonResource.fromAssetId(3957),
}),
});
// 设置时间轴基本信息
this.cesiumViewer.clock.startTime = this.startTime.clone(); // 设置开始时间
this.cesiumViewer.clock.currentTime = this.startTime.clone(); // 设置当前时间
this.cesiumViewer.clock.stopTime = this.stopTime.clone(); // 设置结束时间
this.cesiumViewer.clock.multiplier = 5; // 设置播放速度
this.cesiumViewer.timeline.zoomTo(this.startTime, this.stopTime); // 设置时间轴范围
this.cesiumViewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; // 循环播放
// 初始化视角(飞行模式)
this.cesiumViewer.camera.flyTo(this.homeView);
页面就会有我们设置的时间线轴
二、航线
1、计算航线
首先要确定两个点位之间的坐标,然后再进行计算航线时间点,如:从A点到B点,就需要计算出两个坐标之间每个时间点对应的坐标。其中会用到几个API或者属性:
1、SampledPositionProperty:是用于表示随时间变化的地理位置的类。它通常用于表示动态的、随时间变化的地理位置,例如飞机、汽车或船只的位置。API传送门
2、JulianDate: CesiumJS 库中的一个类,用于表示日期和时间,其中addSeconds方法是添加指定的秒数。API传送门
代码:
computeFly() {
const property = new Cesium.SampledPositionProperty(); // 定义位置属性
// 起点
const startPoint = [119.966746, 30.270928];
// 终点
const endPoint = [120.220684, 30.26294];
this.height = 1000;
const length = 100;
const lonAvg = (endPoint[0] - startPoint[0]) / length; // 经度差值
const latAvg = (endPoint[1] - startPoint[1]) / length; // 纬度差值
for (let index = 0; index < length; index++) {
const time = Cesium.JulianDate.addSeconds(this.startTime, 3.6 * index, new Cesium.JulianDate()); // 计算时间
const position = Cesium.Cartesian3.fromDegrees(startPoint[0] + lonAvg * index, startPoint[1] + latAvg * index, this.height); // 计算位置
property.addSample(time, position);
}
return property;
}
三、加载3D飞机模型并添加航线
加载模型前面有很多方法可以实现,这里我们采用entity方式来加载。创建模型时把位置,航线,时间间隔集合加上。
createModel() {
// 计算路线
const property = this.computeFly();
// 取出其中的位置向量
const orientation = new Cesium.VelocityOrientationProperty(property);
// 取第一个位置向量,并改变相对位置(自己 new 一个也可以)
const viewFrom = orientation.getValue(this.startTime);
// 相对 entity 的视角坐标(迪卡尔4系坐标,可根据 entity 大小设置)
viewFrom.z = 1466.61814287398; // 相对上下
viewFrom.x = -3306.272590514738; // 相对前后
viewFrom.y = 135.03403439279646; // 相对左右
const entity = this.cesiumViewer.entities.add({
name: 'air',
availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({ start: this.startTime, stop: this.stopTime })]), // 时间间隔区间
position: property, // 模型位置
orientation: orientation, // 模型方向
viewFrom: viewFrom, // 视角坐标
model: {
uri: require('../../public/static/feiji.glb'),
minimumPixelSize: 128, // 模型最小像素尺寸
maximumScale: 20000, // 模型最大像素尺寸
},
// 模型轨迹
path: {
resolution: 1,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.1,
color: Cesium.Color.YELLOW.withAlpha(1),
}),
width: 3,
},
});
// 设置 viewer 一直跟随 entity
this.cesiumViewer.trackedEntity = entity;
// 开启时间动画
this.cesiumViewer.clock.shouldAnimate = true;
},
演示
在最后可以调用时钟的开启时钟动画来让飞机沿着我们航线动起来:shouldAnimate = true;
全部代码
<template>
<div class="home">
<div id="cesiumContainer" class="cesium-box" />
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
cesiumViewer: null,
homeView: null,
startTime: null,
stopTime: null,
height: 4000,
};
},
created() {
this.homeView = {
destination: Cesium.Cartesian3.fromDegrees(119.966746, 30.270528, this.height),
orientation: {
// 航向
heading: Cesium.Math.toRadians(0),
// 俯仰
pitch: Cesium.Math.toRadians(60),
// 滚转
roll: 0.0,
},
// camera 初始化后回调函数
complete: this.createModel,
};
this.startTime = Cesium.JulianDate.fromDate(new Date('2025-02-18 16:00:00'));
this.stopTime = Cesium.JulianDate.addSeconds(this.startTime, 360, new Cesium.JulianDate());
},
mounted() {
// 加载地图
this.cesiumViewer = new Cesium.Viewer('cesiumContainer', {
infoBox: false,
baseLayerPicjer: false,
// 设置地形实体化
terrainProvider: new Cesium.CesiumTerrainProvider({
url: Cesium.IonResource.fromAssetId(3957),
}),
});
// 设置时间轴基本信息
this.cesiumViewer.clock.startTime = this.startTime.clone(); // 设置开始时间
this.cesiumViewer.clock.currentTime = this.startTime.clone(); // 设置当前时间
this.cesiumViewer.clock.stopTime = this.stopTime.clone(); // 设置结束时间
this.cesiumViewer.clock.multiplier = 5; // 设置播放速度
this.cesiumViewer.timeline.zoomTo(this.startTime, this.stopTime); // 设置时间轴范围
this.cesiumViewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP; // 循环播放
// 初始化视角(飞行模式)
this.cesiumViewer.camera.flyTo(this.homeView);
// 重写 homeButton 事件
this.cesiumViewer.homeButton.viewModel.command.beforeExecute.addEventListener((e) => {
// 阻止事件继续传递
e.cancel = true;
this.cesiumViewer.camera.flyTo(this.homeView);
});
},
methods: {
createModel() {
// 计算路线
const property = this.computeFly();
// 取出其中的位置向量
const orientation = new Cesium.VelocityOrientationProperty(property);
// 取第一个位置向量,并改变相对位置(自己 new 一个也可以)
const viewFrom = orientation.getValue(this.startTime);
// 相对 entity 的视角坐标(迪卡尔4系坐标,可根据 entity 大小设置)
viewFrom.z = 1466.61814287398; // 相对上下
viewFrom.x = -3306.272590514738; // 相对前后
viewFrom.y = 135.03403439279646; // 相对左右
const entity = this.cesiumViewer.entities.add({
name: 'air',
availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({ start: this.startTime, stop: this.stopTime })]),// 时间区间
position: property, // 模型位置
orientation: orientation, // 模型方向
viewFrom: viewFrom, // 视角坐标
model: {
uri: require('../../public/static/feiji.glb'),
minimumPixelSize: 128, // 模型最小像素尺寸
maximumScale: 20000, // 模型最大像素尺寸
},
// 模型轨迹
path: {
resolution: 1,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.1,
color: Cesium.Color.YELLOW.withAlpha(1),
}),
width: 3,
},
});
// 设置 viewer 一直跟随 entity
this.cesiumViewer.trackedEntity = entity;
// 开启时间动画
this.cesiumViewer.clock.shouldAnimate = true;
},
computeFly() {
const property = new Cesium.SampledPositionProperty(); // 定义位置属性
// 起点:杭州城西
const startPoint = [119.966746, 30.270928];
// 终点:钱江新城
const endPoint = [120.220684, 30.26294];
this.height = 1000;
const length = 100;
const lonAvg = (endPoint[0] - startPoint[0]) / length; // 经度差值
const latAvg = (endPoint[1] - startPoint[1]) / length; // 纬度差值
for (let index = 0; index < length; index++) {
const time = Cesium.JulianDate.addSeconds(this.startTime, 3.6 * index, new Cesium.JulianDate()); // 计算时间
const position = Cesium.Cartesian3.fromDegrees(startPoint[0] + lonAvg * index, startPoint[1] + latAvg * index, this.height); // 计算位置
property.addSample(time, position);
}
return property;
},
},
};
</script>
<style scoped lang="scss">
.home {
height: 100vh;
#cesiumContainer {
height: 100%;
border: 1px solid gray;
}
}
</style>