Система обработки событий мыши в CesiumJS построена вокруг абстракции экранных событий, которые преобразуются в взаимодействие с 3D-сценой, примитивами и сущностями. Основной задачей этой системы является связывание координат указателя на canvas с объектами в виртуальном глобусе и сцене.
CesiumJS работает не с DOM-событиями напрямую, а с собственной
системой ScreenSpaceEventHandler, которая интерпретирует
действия мыши и сенсора в контексте WebGL-контента.
Ключевой принцип:
экранные координаты → луч в сцене → пересечение с объектами
Каждое событие мыши преобразуется в:
Основной объект для обработки мыши:
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
Он привязывается к HTML canvas, на котором рендерится сцена.
Регистрация события выполняется через:
handler.setInputAction(function (event) {
console.log(event.position);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
Внутренняя архитектура:
CesiumJS поддерживает набор стандартных событий:
LEFT_CLICKRIGHT_CLICKMIDDLE_CLICKCesium.ScreenSpaceEventType.LEFT_CLICK
Cesium.ScreenSpaceEventType.RIGHT_CLICK
MOUSE_MOVEИспользуется для hover-интеракций, подсветки объектов и динамического выбора.
handler.setInputAction(function (movement) {
console.log(movement.endPosition);
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
LEFT_DOWNLEFT_UPRIGHT_DOWNRIGHT_UPЭти события применяются для реализации drag-and-drop логики.
WHEEL — прокрутка колеса мышиhandler.setInputAction(function (delta) {
console.log(delta);
}, Cesium.ScreenSpaceEventType.WHEEL);
Каждое событие содержит экранные координаты:
{
position: Cesium.Cartesian2,
startPosition: Cesium.Cartesian2,
endPosition: Cesium.Cartesian2
}
x, y // координаты на canvas
Важно учитывать:
Для работы с реальным миром требуется преобразование экранных координат в географические.
const ray = viewer.camera.getPickRay(event.position);
const position = viewer.scene.globe.pick(ray, viewer.scene);
Результат — Cartesian3 (мировые координаты).
const cartographic = Cesium.Cartographic.fromCartesian(position);
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
const height = cartographic.height;
CesiumJS предоставляет механизм выбора объектов под курсором.
const pickedObject = viewer.scene.pick(event.position);
Возвращает:
if (Cesium.defined(pickedObject)) {
console.log(pickedObject.id);
}
const objects = viewer.scene.drillPick(event.position);
Используется при перекрывающихся объектах.
Entity API тесно интегрирован с системой событий.
handler.setInputAction(function (event) {
const picked = viewer.scene.pick(event.position);
if (Cesium.defined(picked) && picked.id) {
console.log(picked.id);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
let lastPicked;
handler.setInputAction(function (movement) {
const picked = viewer.scene.pick(movement.endPosition);
if (Cesium.defined(lastPicked)) {
lastPicked.color = Cesium.Color.WHITE;
}
if (Cesium.defined(picked) && picked.id) {
lastPicked = picked.id;
lastPicked.color = Cesium.Color.YELLOW;
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
Реализация перетаскивания строится на комбинации событий:
let isDragging = false;
handler.setInputAction(function (event) {
isDragging = true;
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
handler.setInputAction(function (event) {
if (!isDragging) return;
const ray = viewer.camera.getPickRay(event.endPosition);
const position = viewer.scene.globe.pick(ray, viewer.scene);
if (Cesium.defined(position)) {
console.log("drag position", position);
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
handler.setInputAction(function () {
isDragging = false;
}, Cesium.ScreenSpaceEventType.LEFT_UP);
Часто события мыши связаны с управлением камерой.
viewer.scene.screenSpaceCameraController.enableRotate = false;
viewer.scene.screenSpaceCameraController.enableTranslate = false;
viewer.scene.screenSpaceCameraController.enableZoom = false;
Это необходимо при реализации кастомных инструментов взаимодействия.
В CesiumJS участвуют три основных системы:
Screen (x, y)
↓
Pick ray
↓
Cartesian3
↓
Cartographic (lon, lat, height)
3D Tiles требуют особого подхода:
const feature = viewer.scene.pick(event.position);
if (feature instanceof Cesium.Cesium3DTileFeature) {
const name = feature.getProperty("name");
console.log(name);
}
Для рельефа используется:
const ray = viewer.camera.getPickRay(event.position);
const position = viewer.scene.globe.pick(ray, viewer.scene);
Если включен terrainProvider, вычисление учитывает
высоту поверхности.
Обработка событий мыши в CesiumJS может становиться узким местом при
высокой частоте MOUSE_MOVE.
Типичные проблемы:
scene.pickОптимизация:
Пример ограничения частоты:
let lastTime = 0;
handler.setInputAction(function (movement) {
const now = performance.now();
if (now - lastTime < 50) return;
lastTime = now;
viewer.scene.pick(movement.endPosition);
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
Низкоуровневый доступ возможен через DOM:
viewer.canvas.addEventListener("contextmenu", function (e) {
e.preventDefault();
});
Однако такой подход не учитывает сцену Cesium и применяется только для вспомогательной логики.
При создании редакторов геометрии взаимодействие мыши комбинируется с Entity API:
handler.setInputAction(function (event) {
const ray = viewer.camera.getPickRay(event.position);
const position = viewer.scene.globe.pick(ray, viewer.scene);
if (Cesium.defined(position)) {
viewer.entities.add({
position: position,
point: {
pixelSize: 10,
color: Cesium.Color.RED
}
});
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
Обработчики требуют явного удаления:
handler.destroy();
или:
handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
Игнорирование этого приводит к накоплению обработчиков и утечкам памяти.
CesiumJS не использует стандартную систему bubbling DOM. Вместо этого применяется:
На мобильных устройствах ScreenSpaceEventHandler интерпретирует:
Эти события мапятся на те же ScreenSpaceEventType, но с иной семантикой входных данных.
События мыши тесно связаны с render loop Cesium:
Поэтому обработка событий часто проектируется как: