Skip to main content

Camera Control

This example demonstrates how to control the viewpoint in a 3D scene using Cesium's Camera API.

Preview

Switch between different city viewpoints via buttons, supporting fly-to animations, instant positioning, view rotation, and other camera operations.

Complete Code

src/components/CameraControl.tsx
import { useEffect, useRef, useState } from 'react'
import * as Cesium from 'cesium'
import './CameraControl.css'

// City location configuration
const CITIES = {
beijing: { lon: 116.4074, lat: 39.9042, height: 800, name: 'Beijing' },
shanghai: { lon: 121.4737, lat: 31.2304, height: 600, name: 'Shanghai' },
chengdu: { lon: 104.0665, lat: 30.5723, height: 500, name: 'Chengdu' },
everest: { lon: 86.9250, lat: 27.9881, height: 15000, name: 'Mount Everest' },
}

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

// Initial view
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()
}
}, [])

// Fly to specified city (with animation)
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, // Flight duration (seconds)
})
}

// Instant positioning (no animation)
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,
},
})
}

// Top-down view
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), // Looking straight down
roll: 0,
},
duration: 1.5,
})
}

// 45-degree perspective view
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>Camera Control</h3>

<div className="section">
<h4>Fly to City</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>View Switch</h4>
<div className="buttons">
<button onClick={handleTopView}>Top View</button>
<button onClick={handlePerspectiveView}>45-Degree View</button>
</div>
</div>
</div>
</div>
)
}

export default CameraControl

Stylesheet

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;
}

Key Code Explanation

1. Instant Positioning with setView

viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(lon, lat, height),
orientation: {
heading: Cesium.Math.toRadians(0), // Heading (0 = true north)
pitch: Cesium.Math.toRadians(-30), // Pitch (negative = looking down)
roll: 0, // Roll
},
})

setView immediately switches the viewpoint without any transition animation.

2. Fly-to Animation with flyTo

viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(lon, lat, height),
orientation: { heading, pitch, roll },
duration: 2.0, // Flight duration (seconds)
})

flyTo smoothly flies from the current position to the target position.

3. View Parameters

ParameterDescriptionRange
headingHorizontal heading0 deg = true north, 90 deg = east
pitchPitch angle-90 deg = straight down, 0 deg = horizontal
rollRoll angleUsually 0

Extended Features

  1. View Bookmarks - Save/restore custom viewpoints
  2. Orbit Flight - Automatically rotate around a target point
  3. Path Flight - Fly along a preset path
  4. Range Limit - Restrict camera movement range