Cesium 快速入门(四)相机控制完全指南
看过的知识不等于学会。唯有用心总结、系统记录,并通过温故知新反复实践,才能真正掌握一二
作为一名摸爬滚打三年的前端开发,开源社区给了我饭碗,我也将所学的知识体系回馈给大家,助你少走弯路!
OpenLayers、Leaflet 快速入门 ,每周保持更新 2 个案例
Cesium 快速入门,每周保持更新 4 个案例
Cesium 快速入门(一)快速搭建项目
Cesium 快速入门(二)底图更换
Cesium 快速入门(三)Viewer:三维场景的"外壳"
Cesium 快速入门(四)相机控制完全指南
Cesium 快速入门(五)坐标系
Cesium 快速入门(六)实体类型介绍
Cesium 快速入门(七)材质详解
Cesium 快速入门(八)Primitive(图元)系统深度解析
Cesium 快速入门(九)Appearance(外观)系统深度解析
Cesium 快速入门(十) JulianDate(儒略日期)详解
Cesium 快速入门(十一)3D Tiles 大规模三维地理空间数据
Cesium 快速入门(十二)数据加载详解
Cesium 快速入门(十三)事件系统
Cesium 相机控制完全指南
相机(Camera
)是 Cesium 场景交互的核心,负责定义观察者视角和运动方式。
核心概念
相机坐标系
Cesium 相机系统基于右手坐标系:
- 位置(Position):相机在世界坐标系中的三维坐标(Cartesian3)
- 方向(Direction):相机视线方向向量(默认指向-Z 轴)
- 姿态(Orientation):由 heading(方位角)、pitch(俯仰角)、roll(翻滚角)定义
重要区别:
- 本地坐标系:以相机为中心,X 轴向右,Y 轴向上,Z 轴向后
- 世界坐标系:以地球中心为原点的固定坐标系
相机状态参数
参数 | 类型 | 描述 | 取值范围 |
---|---|---|---|
position |
Cartesian3 | 世界坐标位置 | 任意三维坐标 |
positionCartographic |
Cartographic | 经纬度高度表示 | 经度[-180,180],纬度[-90,90],高度 ≥0 |
heading |
Number | 方位角(绕 Y 轴) | [-π, π]弧度,0 为正北 |
pitch |
Number | 俯仰角(绕 X 轴) | [-π/2, π/2]弧度,-π/2 为俯视 |
roll |
Number | 翻滚角(绕 Z 轴) | [-π, π]弧度,0 为水平 |
基础配置
默认视角设置
相机初始化默认看向 0° 经线、0° 纬线区域,可通过以下方式修改默认视图:
js
// 在创建Viewer前设置全局默认视图矩形
Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(
89.99, // 西经度
39.9, // 南纬度
116.41, // 东经度
39.92 // 北纬度
);
注意 :
DEFAULT_VIEW_RECTANGLE
必须在 Viewer 创建前设置才有效,适用于需要固定初始范围的场景(如特定区域监控)
相机参数获取
js
const camera = viewer.camera;
// 获取相机位置(笛卡尔坐标)
const position = camera.position;
// 获取相机方向矩阵
const directionMatrix = camera.direction;
// 获取相机方位角
const heading = camera.heading;
// 获取相机俯仰角
const pitch = camera.pitch;
// 获取相机滚动角
const roll = camera.roll;
// 获取相机高度(米)
const height = camera.positionCartographic.height;
相机控制方法
1. 直接定位(setView)
瞬时定位到目标位置,无过渡动画,适用于需要精确定位的场景:
js
// 方式一:使用heading/pitch/roll定义姿态
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(
116.404, // 经度
39.915, // 纬度
1000 // 高度(米)
),
orientation: {
heading: Cesium.Math.toRadians(0), // 方位角:0°(正北)
pitch: Cesium.Math.toRadians(-30), // 俯仰角:-30°(俯视)
roll: 0, // 翻滚角:0°(水平)
},
});
// 方式二:使用方向向量定义姿态
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(116.404, 39.915, 1000),
orientation: {
direction: new Cesium.Cartesian3(0.1, -0.2, -1), // 视线方向向量
up: new Cesium.Cartesian3(0, 1, 0), // 相机上方向向量
},
});
应用场景:初始化定位、按钮快速跳转、精确坐标定位
2. 平滑飞行(flyTo)
带过渡动画的相机移动,提供更好的用户体验:
js
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(116.404, 39.915, 1000),
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-30),
roll: 0,
},
duration: 5, // 动画持续时间(秒),默认3秒
easingFunction: Cesium.EasingFunction.CUBIC_IN_OUT, // 缓动函数
maximumHeight: 2000, // 飞行路径最高点限制(米)
complete: function () {
console.log("飞行完成!");
},
cancel: function () {
console.log("飞行被取消!");
},
endTransform: Cesium.Matrix4.IDENTITY, // 结束时的变换矩阵
});
常用缓动函数对比:
LINEAR_NONE
:匀速运动CUBIC_IN_OUT
:先加速后减速(默认)QUADRATIC_IN
:匀加速ELASTIC_OUT
:弹性效果
3. 锁定目标(lookAt)
固定相机看向目标点,适用于跟踪移动目标:
js
// 方式一:使用Cartesian3偏移量
viewer.camera.lookAt(
Cesium.Cartesian3.fromDegrees(116.404, 39.915, 0), // 目标点坐标
new Cesium.Cartesian3(0, -500, 300) // 相对目标点的偏移量(右、后、上)
);
// 方式二:使用HeadingPitchRange(推荐)
viewer.camera.lookAt(
Cesium.Cartesian3.fromDegrees(116.404, 39.915, 0),
new Cesium.HeadingPitchRange(
Cesium.Math.toRadians(30), // 方位角:30°(东北方向)
Cesium.Math.toRadians(-20), // 俯仰角:-20°(俯视)
1000 // 距离目标点的距离(米)
)
);
注意 :使用 lookAt 后,相机将锁定目标点,需调用
camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
恢复自由控制
交互控制
方法 | 描述 | 单位:米 | 类型:Number |
---|---|---|---|
moveForward |
向前移动 | ||
moveBackward |
向后移动 | ||
moveLeft |
向左移动 | ||
moveRight |
向右移动 | ||
moveUp |
向上移动 | ||
moveDown |
向下移动 | ||
---------- | ----- | ------ | --------- |
lookLeft |
向左旋转 | ||
lookRight |
向右旋转 | ||
lookUp |
向上旋转 | ||
lookDown |
向下旋转 | ||
---------- | ----- | ------ | --------- |
twistLeft |
向左倾斜 | ||
twistRight |
向右倾斜 |
键盘控制实现
完整的相机键盘控制方案,支持移动、旋转和倾斜:
vue
<template>
<div ref="cesiumContainer" class="container"></div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import * as Cesium from "cesium";
const cesiumContainer = ref(null);
let viewer = null;
// 天地图TOKEN
const token = "05be06461004055923091de7f3e51aa6";
onMounted(() => {
// 初始化Viewer
viewer = new Cesium.Viewer(cesiumContainer.value, {
geocoder: false, // 关闭地理编码搜索
homeButton: false, // 关闭主页按钮
sceneModePicker: false, // 关闭场景模式选择器
baseLayerPicker: false, // 关闭底图选择器
navigationHelpButton: false, // 关闭导航帮助
animation: false, // 关闭动画控件
timeline: false, // 关闭时间轴
fullscreenButton: false, // 关闭全屏按钮
baseLayer: false, // 关闭默认地图
});
// 清空logo
viewer.cesiumWidget.creditContainer.style.display = "none";
// 监听键盘事件
document.addEventListener("keydown", function (event) {
const camera = viewer.camera;
const distance = 100; // 每次移动的距离
switch (event.key) {
case "ArrowUp": // 上按键
camera.moveForward(distance); // 向前移动
break;
case "ArrowDown": // 下按键
camera.moveBackward(distance); // 向后移动
break;
case "ArrowLeft": // 左按键
camera.moveLeft(distance); // 向左移动
break;
case "ArrowRight": // 右按键
camera.moveRight(distance); // 向右移动
break;
case "w": // w按键
camera.lookUp(distance); // 向上旋转
break;
case "s": // s按键
camera.lookDown(distance); // 向下旋转
break;
case "a": // a按键
camera.lookLeft(distance); // 向左旋转
break;
case "d": // d按键
camera.lookRight(distance); // 向右旋转
break;
case "q": // q按键
camera.twistLeft(distance); // 向左倾斜
break;
case "e": // e按键
camera.twistRight(distance); // 向右倾斜
break;
default:
break;
}
});
initMap();
});
// 加载天地图
const initMap = () => {
// 以下为天地图及天地图标注加载
const tiandituProvider = new Cesium.WebMapTileServiceImageryProvider({
url:
"http://{s}.tianditu.gov.cn/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=" +
token,
layer: "img",
style: "default",
format: "tiles",
tileMatrixSetID: "w", // 天地图使用 Web 墨卡托投影(EPSG:3857),需确保 tileMatrixSetID: "w"
subdomains: ["t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7"], // 子域名
maximumLevel: 18,
credit: new Cesium.Credit("天地图影像"),
});
// 添加地理标注
const labelProvider = new Cesium.WebMapTileServiceImageryProvider({
url:
"http://{s}.tianditu.gov.cn/cia_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=cia&tileMatrixSet=w&tileMatrix={TileMatrix}&tileRow={TileRow}&tileCol={TileCol}&style=default&format=tiles&tk=" +
token,
layer: "img",
style: "default",
format: "tiles",
tileMatrixSetID: "w",
subdomains: ["t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7"], // 子域名轮询
maximumLevel: 18,
credit: new Cesium.Credit("天地图影像"),
});
// 天地图影像添加到viewer实例的影像图层集合中
viewer.imageryLayers.addImageryProvider(tiandituProvider);
// 天地图地理标注(后添加的会覆盖前面的)
viewer.imageryLayers.addImageryProvider(labelProvider);
};
</script>
<style scoped>
.container {
width: 100vw;
height: 100vh;
}
</style>
高级应用
相机事件监听
监听相机状态变化,实现动态响应:
js
// 相机移动开始事件
viewer.camera.moveStart.addEventListener(function () {
console.log("相机开始移动");
// 可在此处暂停其他动画或更新UI状态
});
// 相机移动结束事件(常用)
viewer.camera.moveEnd.addEventListener(function () {
console.log("相机移动结束");
// 获取新视域范围
const viewRectangle = viewer.camera.computeViewRectangle();
if (Cesium.defined(viewRectangle)) {
const west = Cesium.Math.toDegrees(viewRectangle.west).toFixed(2);
const east = Cesium.Math.toDegrees(viewRectangle.east).toFixed(2);
const south = Cesium.Math.toDegrees(viewRectangle.south).toFixed(2);
const north = Cesium.Math.toDegrees(viewRectangle.north).toFixed(2);
console.log(`当前视域范围: ${west}°E-${east}°E, ${south}°N-${north}°N`);
}
});
视角保存与恢复
实现视角状态的保存和快速切换:
js
// 视角状态管理
const cameraStates = {
savedStates: {}, // 存储视角状态的对象
// 保存当前视角
saveState: function (key) {
const camera = viewer.camera;
this.savedStates[key] = {
position: camera.position.clone(),
orientation: {
heading: camera.heading,
pitch: camera.pitch,
roll: camera.roll,
},
};
console.log(`已保存视角: ${key}`);
},
// 恢复视角
restoreState: function (key, duration = 1.5) {
const state = this.savedStates[key];
if (!state) {
console.error(`未找到保存的视角: ${key}`);
return;
}
viewer.camera.flyTo({
destination: state.position,
orientation: state.orientation,
duration: duration,
});
},
// 删除视角
deleteState: function (key) {
if (this.savedStates[key]) {
delete this.savedStates[key];
console.log(`已删除视角: ${key}`);
}
},
};
// 使用示例
// cameraStates.saveState("overview"); // 保存概览视角
// cameraStates.restoreState("overview"); // 恢复概览视角
性能优化
相机相关性能建议
使用相机视锥体剔除:自动隐藏视域外对象
js
viewer.scene.globe.enableLighting = true;
viewer.scene.globe.depthTestAgainstTerrain = true;
避免过度相机移动:高频相机移动会导致帧率下降
js
// 相机移动节流
let isCameraMoving = false;
viewer.camera.moveEnd.addEventListener(function () {
isCameraMoving = false;
// 恢复高帧率渲染
viewer.scene.maximumRenderTimeChange = 0.0;
});
viewer.camera.moveStart.addEventListener(function () {
isCameraMoving = true;
// 移动时降低渲染频率
viewer.scene.maximumRenderTimeChange = 0.2;
});