В основе системы управления в CesiumJS лежит камера, связанная с виртуальной моделью Земли. Камера описывается через позицию в мировых координатах, ориентацию (heading, pitch, roll) и набор ограничений, накладываемых сценой. Управление пользователем не взаимодействует напрямую с WebGL-сценой — оно транслируется в изменения параметров камеры через слой контроллеров.
Контроллер камеры включает:
Базовая архитектура построена вокруг
ScreenSpaceCameraController, который по умолчанию подключён
к Viewer.
const viewer = new Cesium.Viewer("cesiumContainer");
const controller = viewer.scene.screenSpaceCameraController;
Этот объект является точкой расширения для большинства задач кастомизации управления.
Каждое действие пользователя в CesiumJS проходит через систему
событий сцены. Основной механизм —
ScreenSpaceEventHandler.
Он позволяет перехватывать:
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
handler.setInputAction((movement) => {
const cartesian = viewer.camera.pickEllipsoid(
movement.position,
viewer.scene.globe.ellipsoid
);
if (cartesian) {
console.log("Координаты мира:", cartesian);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
Этот слой позволяет внедрять собственную логику до того, как управление попадёт в стандартный контроллер камеры.
Кастомизация часто начинается с отключения встроенных взаимодействий. CesiumJS позволяет детально управлять тем, какие типы ввода активны.
const controller = viewer.scene.screenSpaceCameraController;
controller.enableRotate = false;
controller.enableTranslate = false;
controller.enableZoom = false;
controller.enableTilt = false;
controller.enableLook = false;
Такой подход полностью блокирует стандартную навигацию, оставляя разработчику возможность построить собственную модель управления.
Создание пользовательского контроллера обычно основано на прямом
управлении объектом Camera.
Камера предоставляет методы:
moveForwardmoveBackwardmoveLeftmoveRightlookAtflyTosetViewПростейшая модель WASD-навигации:
const camera = viewer.camera;
const movement = {
forward: false,
backward: false,
left: false,
right: false
};
document.addEventListener("keydown", (e) => {
if (e.code === "KeyW") movement.forward = true;
if (e.code === "KeyS") movement.backward = true;
if (e.code === "KeyA") movement.left = true;
if (e.code === "KeyD") movement.right = true;
});
document.addEventListener("keyup", (e) => {
if (e.code === "KeyW") movement.forward = false;
if (e.code === "KeyS") movement.backward = false;
if (e.code === "KeyA") movement.left = false;
if (e.code === "KeyD") movement.right = false;
});
viewer.scene.preUpdate.addEventListener(() => {
const speed = 10.0;
if (movement.forward) camera.moveForward(speed);
if (movement.backward) camera.moveBackward(speed);
if (movement.left) camera.moveLeft(speed);
if (movement.right) camera.moveRight(speed);
});
Использование preUpdate гарантирует синхронизацию с
рендер-циклом сцены.
Одной из ключевых задач пользовательских контроллеров является корректное преобразование координат.
CesiumJS работает с несколькими системами:
const scene = viewer.scene;
handler.setInputAction((movement) => {
const ray = viewer.camera.getPickRay(movement.position);
const position = scene.globe.pick(ray, scene);
if (position) {
const cartographic = Cesium.Cartographic.fromCartesian(position);
console.log(
Cesium.Math.toDegrees(cartographic.longitude),
Cesium.Math.toDegrees(cartographic.latitude)
);
}
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
Этот механизм лежит в основе всех кастомных навигационных систем, включая selection tools, measurement tools и terrain interaction.
Плавность управления часто достигается через инерционную модель, где скорость постепенно затухает.
let velocity = new Cesium.Cartesian3(0, 0, 0);
viewer.scene.preUpdate.addEventListener(() => {
const damping = 0.9;
camera.position = Cesium.Cartesian3.add(
camera.position,
velocity,
new Cesium.Cartesian3()
);
velocity = Cesium.Cartesian3.multiplyByScalar(
velocity,
damping,
new Cesium.Cartesian3()
);
});
Инерция позволяет отделить момент ввода от реакции камеры, создавая независимую модель движения.
Стандартное вращение камеры основано на изменении heading и pitch. При кастомизации часто требуется заменить эту модель на сферическую или орбитальную.
let isRotating = false;
let lastPosition;
handler.setInputAction((movement) => {
isRotating = true;
lastPosition = movement.position;
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
handler.setInputAction(() => {
isRotating = false;
}, Cesium.ScreenSpaceEventType.LEFT_UP);
handler.setInputAction((movement) => {
if (!isRotating) return;
const deltaX = movement.position.x - lastPosition.x;
const deltaY = movement.position.y - lastPosition.y;
camera.heading += deltaX * 0.005;
camera.pitch += deltaY * 0.005;
lastPosition = movement.position;
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
Такая модель даёт полный контроль над углами обзора и позволяет внедрять ограничения на вертикальный угол.
Частый сценарий — камера вращается вокруг фиксированной цели.
const target = Cesium.Cartesian3.fromDegrees(30, 50, 0);
viewer.scene.preUpdate.addEventListener(() => {
const radius = 100000.0;
const offset = new Cesium.Cartesian3(
radius * Math.cos(camera.heading),
radius * Math.sin(camera.heading),
radius * Math.sin(camera.pitch)
);
camera.position = Cesium.Cartesian3.add(target, offset, new Cesium.Cartesian3());
camera.lookAt(target);
});
Подобная схема используется в аналитических и визуализационных интерфейсах, где важно удерживать фокус на объекте.
Сенсорное управление в CesiumJS обрабатывается через те же абстракции событий, но с дополнительными типами ввода.
Основные жесты:
Кастомизация жестов обычно требует отключения встроенного поведения:
controller.enableZoom = false;
controller.enableRotate = false;
Далее реализуется собственная интерпретация касаний через
ScreenSpaceEventHandler.
Контроллеры часто включают ограничения, чтобы предотвратить выход камеры за допустимые границы.
Пример ограничения высоты:
viewer.scene.preUpdate.addEventListener(() => {
const minHeight = 1000;
const cartographic = Cesium.Cartographic.fromCartesian(camera.position);
if (cartographic.height < minHeight) {
cartographic.height = minHeight;
camera.position = Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
cartographic.height
);
}
});
Ограничения применяются как на позицию, так и на углы обзора.
Пользовательский контроллер в CesiumJS обычно организуется как слой над стандартным API:
Состояния позволяют отделить режимы:
const modes = {
FREE: 0,
ORBIT: 1,
PICK: 2
};
let currentMode = modes.FREE;
Переключение режимов меняет интерпретацию одинаковых событий ввода.
Контроллер должен синхронизироваться с жизненным циклом рендера CesiumJS. Основные точки интеграции:
scene.preUpdate — до рендера;scene.postUpdate — после обновления сцены;Clock.onTick — синхронизация с временем.Использование preUpdate обеспечивает стабильность при
изменении состояния камеры:
viewer.scene.preUpdate.addEventListener((scene, time) => {
// обновление состояния контроллера
});
В более сложных сценариях стандартный контроллер не используется
вовсе, а заменяется собственной реализацией логики, где
ScreenSpaceCameraController остаётся отключённым.
Такой подход характерен для:
В этих системах камера становится полностью управляемым объектом без встроенной интерпретации пользовательского ввода.