相机控制
这个示例演示如何使用 Cesium 的相机 API 控制 3D 场景中的视角。
效果预览
通过按钮切换不同城市视角,支持飞行动画、即时定位、视角旋转等相机操作。
完整代码
src/components/CameraControl.tsx
import { useEffect, useRef, useState } from 'react'
import * as Cesium from 'cesium'
import './CameraControl.css'
// 城市位置配置
const CITIES = {
beijing: { lon: 116.4074, lat: 39.9042, height: 800, name: '北京' },
shanghai: { lon: 121.4737, lat: 31.2304, height: 600, name: '上海' },
chengdu: { lon: 104.0665, lat: 30.5723, height: 500, name: '成都' },
everest: { lon: 86.9250, lat: 27.9881, height: 15000, name: '珠穆朗玛峰' },
}
function CameraControl() {
const containerRef = useRef<HTMLDivElement>(null)
const viewerRef = useRef<Cesium.Viewer | null>(null)
const [currentCity, setCurrentCity] = useState('beijing')
useEffect(() => {
if (!containerRef.current) return
Cesium.Ion.defaultAccessToken = 'your-cesium-ion-token'
const viewer = new Cesium.Viewer(containerRef.current, {
animation: false,
fullscreenButton: false,
geocoder: false,
homeButton: false,
sceneModePicker: false,
selectionIndicator: false,
timeline: false,
navigationHelpButton: false,
infoBox: false,
baseLayerPicker: false,
terrain: Cesium.Terrain.fromWorldTerrain(),
})
viewerRef.current = viewer
// 初始视角
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(116.4074, 39.9042, 800),
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-30),
roll: 0,
},
})
return () => {
viewer.destroy()
}
}, [])
// 飞行到指定城市(带动画)
const handleFlyTo = (cityKey: string) => {
const viewer = viewerRef.current
if (!viewer) return
const city = CITIES[cityKey as keyof typeof CITIES]
setCurrentCity(cityKey)
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(city.lon, city.lat, city.height),
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-30),
roll: 0,
},
duration: 2.0, // 飞行时间(秒)
})
}
// 即时定位(无动画)
const handleSetView = (cityKey: string) => {
const viewer = viewerRef.current
if (!viewer) return
const city = CITIES[cityKey as keyof typeof CITIES]
setCurrentCity(cityKey)
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(city.lon, city.lat, city.height),
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-30),
roll: 0,
},
})
}
// 俯视角
const handleTopView = () => {
const viewer = viewerRef.current
if (!viewer) return
const city = CITIES[currentCity as keyof typeof CITIES]
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(city.lon, city.lat, city.height * 3),
orientation: {
heading: 0,
pitch: Cesium.Math.toRadians(-90), // 正下方看
roll: 0,
},
duration: 1.5,
})
}
// 45度视角
const handlePerspectiveView = () => {
const viewer = viewerRef.current
if (!viewer) return
const city = CITIES[currentCity as keyof typeof CITIES]
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(city.lon, city.lat, city.height),
orientation: {
heading: Cesium.Math.toRadians(45),
pitch: Cesium.Math.toRadians(-45),
roll: 0,
},
duration: 1.5,
})
}
return (
<div className="camera-control">
<div ref={containerRef} className="cesium-container" />
<div className="control-panel">
<h3>相机控制</h3>
<div className="section">
<h4>飞行到城市</h4>
<div className="buttons">
{Object.entries(CITIES).map(([key, city]) => (
<button key={key} onClick={() => handleFlyTo(key)}>
{city.name}
</button>
))}
</div>
</div>
<div className="section">
<h4>视角切换</h4>
<div className="buttons">
<button onClick={handleTopView}>俯视</button>
<button onClick={handlePerspectiveView}>45度视角</button>
</div>
</div>
</div>
</div>
)
}
export default CameraControl
样式文件
src/components/CameraControl.css
.camera-control {
position: relative;
width: 100%;
height: 100vh;
}
.cesium-container {
width: 100%;
height: 100%;
}
.control-panel {
position: absolute;
top: 20px;
right: 20px;
background: rgba(255, 255, 255, 0.95);
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
min-width: 220px;
}
.control-panel h3 {
margin: 0 0 15px 0;
font-size: 18px;
}
.section {
margin-bottom: 15px;
}
.section h4 {
margin: 0 0 8px 0;
font-size: 14px;
color: #666;
}
.buttons {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.buttons button {
padding: 6px 14px;
border: none;
border-radius: 4px;
background: #1890ff;
color: white;
cursor: pointer;
font-size: 13px;
}
.buttons button:hover {
background: #40a9ff;
}
关键代码说明
1. 即时定位 setView
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(lon, lat, height),
orientation: {
heading: Cesium.Math.toRadians(0), // 朝向(0=正北)
pitch: Cesium.Math.toRadians(-30), // 俯仰角(负值=向下看)
roll: 0, // 翻滚角
},
})
setView 立即切换视角,没有过渡动画。
2. 飞行动画 flyTo
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(lon, lat, height),
orientation: { heading, pitch, roll },
duration: 2.0, // 飞行时间(秒)
})
flyTo 会从当前位置平滑飞行到目标位置。
3. 视角参数
| 参数 | 说明 | 范围 |
|---|---|---|
heading | 水平朝向 | 0° = 正北,90° = 正东 |
pitch | 俯仰角 | -90° = 正下方,0° = 水平 |
roll | 翻滚角 | 通常为 0 |
扩展功能
- 视角书签 - 保存/恢复自定义视角
- 环绕飞行 - 围绕目标点自动旋转
- 路径飞行 - 沿预设路径飞行
- 限制范围 - 限制相机移动范围