Доступность в CesiumJS опирается на фундаментальное противоречие
между WebGL-рендерингом и семантическим веб-интерфейсом: сцена
отображается через <canvas>, который сам по себе не
содержит доступного DOM-дерева, что делает невозможным прямое восприятие
объектов экранными дикторами и классическими средствами навигации без
дополнительного слоя абстракции.
WebGL в CesiumJS работает как отрисовка пикселей, а не структурированных элементов интерфейса. Это означает:
Поэтому доступность в CesiumJS строится не вокруг самой сцены, а вокруг вспомогательных интерфейсов и программной семантики.
Доступность достигается через дополнительный HTML-слой поверх canvas:
Каждый интерактивный элемент должен иметь корректные ARIA-атрибуты:
role="button" для кастомных кнопок;aria-label для описания действий;aria-expanded для раскрывающихся панелей;aria-live для динамически обновляемых данных.Пример структурирования UI:
<div id="toolbar">
<button aria-label="Приблизить камеру">Zoom In</button>
<button aria-label="Отдалить камеру">Zoom Out</button>
</div>
<div id="infoPanel" aria-live="polite"></div>
Cesium UI по умолчанию не полностью ориентирован на WCAG-совместимость, поэтому кастомизация интерфейса является ключевым элементом.
Камера и управление сценой должны быть доступны через клавиатуру независимо от мыши.
Основные направления:
+ / - — масштабирование;Shift + стрелки — изменение наклона;Home — возврат к глобальному виду;Tab — переключение элементов интерфейса.Пример привязки кастомных клавиш:
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
document.addEventListener("keydown", (e) => {
switch (e.key) {
case "+":
viewer.camera.zoomIn(10000);
break;
case "-":
viewer.camera.zoomOut(10000);
break;
case "ArrowUp":
viewer.camera.pitch += 0.05;
break;
}
});
Ключевая проблема — синхронизация состояния камеры и доступного описания происходящего. Без этого пользователь вспомогательных технологий не понимает, что изменилось.
Камера в CesiumJS является центральным элементом взаимодействия, но она не имеет семантического эквивалента в DOM.
Для повышения доступности важно:
Пример обновления доступного статуса:
viewer.camera.moveEnd.addEventListener(() => {
const height = viewer.camera.positionCartographic.height;
document.getElementById("infoPanel").textContent =
`Высота камеры: ${Math.round(height)} м`;
});
При этом важно избегать избыточных обновлений, чтобы screen reader не перегружался постоянными уведомлениями.
В CesiumJS сущности (entities) являются основным способом добавления семантики к геометрии.
Каждая сущность должна иметь:
name);description);id).Это позволяет формировать доступное представление сцены вне canvas.
viewer.entities.add({
id: "building-1",
name: "Главное здание",
description: "Офисный центр в центральной части города",
position: Cesium.Cartesian3.fromDegrees(71.4304, 51.1282),
point: { pixelSize: 10 }
});
Далее эти данные используются для:
При выборе объекта необходимо явно уведомлять assistive technologies.
viewer.selectedEntityChanged.addEventListener((entity) => {
const panel = document.getElementById("infoPanel");
if (!entity) {
panel.textContent = "Объект не выбран";
return;
}
panel.textContent = `${entity.name}: ${entity.description}`;
});
Без этого события визуальная интеракция остаётся полностью недоступной.
Cesium canvas часто перехватывает события мыши и клавиатуры, поэтому требуется явное управление фокусом:
viewer.canvas.setAttribute("tabindex", "0");
viewer.canvas.addEventListener("focus", () => {
console.log("Canvas в фокусе");
});
В сложных интерфейсах canvas часто исключается из tab-навигации полностью, а управление выносится в UI.
3D-сцены CesiumJS подвержены проблемам:
Для повышения доступности применяются:
viewer.scene.highDynamicRange = false;
viewer.scene.globe.enableLighting = false;
Также важно учитывать пользователей с дальтонизмом: цвет не должен быть единственным маркером состояния объектов.
CesiumJS поддерживает временную модель сцены, что усложняет доступность из-за постоянных изменений.
Проблема:
Решение:
viewer.clock.multiplier = 0; // пауза для анализа состояния
Механизм picking (выбор объектов под курсором) необходимо дублировать через список:
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
handler.setInputAction((movement) => {
const picked = viewer.scene.pick(movement.position);
if (picked && picked.id) {
viewer.selectedEntity = picked.id;
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
Однако этого недостаточно — требуется параллельный список объектов с возможностью выбора через клавиатуру.
Создание отдельного DOM-представления сцены:
<ul id="entityList" role="listbox"></ul>
const list = document.getElementById("entityList");
viewer.entities.values.forEach((e) => {
const item = document.createElement("li");
item.setAttribute("tabindex", "0");
item.textContent = e.name;
item.addEventListener("click", () => {
viewer.selectedEntity = e;
});
list.appendChild(item);
});
Такой подход превращает 3D-сцену в доступную структуру данных.
Динамические изменения сцены должны транслироваться через
aria-live:
<div id="status" aria-live="polite"></div>
document.getElementById("status").textContent =
"Загружены новые тайлы местности";
Чрезмерное использование assertive-режима приводит к перегрузке экранных дикторов и должно избегаться.
Стандартный интерфейс Cesium включает элементы управления, не всегда соответствующие требованиям доступности:
Поэтому промышленная практика заключается в:
viewer.animation.container.style.display = "none";
viewer.timeline.container.style.display = "none";
Основной принцип доступности в CesiumJS — дублирование визуальной сцены в структурированные данные:
3D-сцена остаётся визуальным слоем, а доступность реализуется через параллельную информационную модель, синхронизированную с состоянием камеры, сущностей и пользовательских действий.