Controller классы

Роль Controller в архитектуре взаимодействия

Controller-классы в Deck.gl отвечают за преобразование пользовательских действий (мышь, клавиатура, жесты) в изменения viewState. Они формируют слой взаимодействия между низкоуровневыми событиями браузера и системой камер, используемой библиотекой визуализации.

Основная задача Controller — управление состоянием камеры без прямого вмешательства в рендеринг слоёв. Каждый Controller инкапсулирует набор правил интерпретации ввода и обновления параметров вида.

Ключевые обязанности:

  • обработка событий pointer/mouse/touch
  • обработка клавиатурного ввода
  • преобразование событий в изменения viewState
  • управление ограничениями (min/max zoom, pitch, bearing)
  • поддержка инерции и сглаживания
  • синхронизация с компонентом Deck

Базовый класс Controller

Все контроллеры наследуются от абстрактного базового класса Controller. Он определяет единый контракт взаимодействия с системой камер.

Типовая структура:

  • onViewStateChange
  • onStateChange
  • handleEvent
  • getViewportProps
  • updateViewport

Controller не хранит визуальных данных; он работает только с состоянием взаимодействия.

Основные параметры конфигурации:

  • dragPan — разрешение перемещения сцены
  • dragRotate — вращение сцены
  • scrollZoom — масштабирование колесом мыши
  • doubleClickZoom — зум по двойному клику
  • touchRotate — поддержка вращения жестами
  • keyboard — управление с клавиатуры

Механизм преобразования событий

Controller получает события в унифицированной форме и преобразует их в изменения параметров камеры:

  • longitude, latitude
  • zoom
  • pitch
  • bearing

События проходят через внутренний pipeline:

  1. Захват события (pointer down / wheel / keydown)
  2. Нормализация координат
  3. Определение типа жеста
  4. Применение правил трансформации
  5. Генерация нового viewState
  6. Передача состояния в Deck

Особое значение имеет накопление дельт (delta) при drag-событиях, что позволяет реализовать плавное перемещение без рывков.


MapController

MapController используется для 2D-картографических сцен и имитирует поведение классических веб-карт.

Основные особенности:

  • перемещение карты по плоскости
  • зум относительно центра экрана
  • вращение вокруг оси Z
  • ограничение по latitude для предотвращения “переворота” карты

Типичная конфигурация:

const controller = {
  dragPan: true,
  scrollZoom: true,
  dragRotate: true,
  minZoom: 0,
  maxZoom: 20,
  minPitch: 0,
  maxPitch: 60
};

MapController активно использует проекцию Web Mercator и учитывает нелинейность масштабирования при высоких zoom-уровнях.


OrbitController

OrbitController предназначен для вращения вокруг фиксированной точки в 3D-пространстве.

Основные параметры:

  • target — центр вращения
  • rotationOrbit — вращение вокруг вертикальной оси
  • rotationX — наклон камеры
  • zoom — приближение к объекту

Модель поведения:

  • левая кнопка мыши — орбитальное вращение
  • правая кнопка — панорамирование
  • колесо — зум вдоль направления камеры

OrbitController часто применяется в инженерной визуализации, 3D-моделировании и анализе данных с фиксированной точкой интереса.

Особенность реализации — отделение трансформации орбиты от мировых координат, что позволяет сохранять стабильный центр вращения даже при изменении масштаба.


FirstPersonController

FirstPersonController моделирует поведение камеры от первого лица, аналогично 3D-играм.

Основные характеристики:

  • свободное перемещение по сцене
  • ограниченный pitch (обычно от -90° до +90°)
  • управление направлением взгляда через мышь
  • движение вперёд/назад через клавиатуру

Ключевые параметры:

  • moveSpeed
  • lookSpeed
  • invertY
  • maxPitch

Алгоритм обновления состояния:

  • yaw изменяется по горизонтальному движению мыши
  • pitch изменяется по вертикальному движению
  • позиция камеры обновляется в соответствии с направлением взгляда

InteractionState и внутренняя модель ввода

Каждый Controller поддерживает interactionState, фиксирующий текущие активные жесты:

  • isDragging
  • isZooming
  • isRotating
  • координаты начального касания
  • накопленные дельты

Эта модель позволяет отделить мгновенные события от длительных жестов, таких как drag или pinch.

InteractionState обеспечивает:

  • корректную обработку многопальцевого ввода
  • предотвращение конфликтов между жестами
  • поддержку инерционного движения

Обработка жестов и мультисенсорного ввода

Controller-классы используют унифицированную модель жестов:

  • drag → pan/rotate
  • pinch → zoom + rotate
  • wheel → zoom
  • keydown → step-based movement

При мультисенсорном вводе вычисляется:

  • центр жеста
  • расстояние между точками касания
  • угол поворота между пальцами

Это позволяет реализовать непрерывное управление камерой без скачков.


Ограничения и clamp-логика

Для предотвращения некорректных состояний применяются ограничения:

  • ограничение zoom (minZoom, maxZoom)
  • ограничение pitch (minPitch, maxPitch)
  • ограничение координат (например, latitude в MapController)

Clamp выполняется после каждого обновления viewState, что гарантирует стабильность состояния камеры.


Синхронизация с Deck

Controller не работает изолированно; он интегрирован в жизненный цикл Deck.gl через props:

  • controller: true | object
  • viewState
  • onViewStateChange

Схема взаимодействия:

  1. пользовательское событие
  2. Controller формирует новый viewState
  3. Deck принимает обновление
  4. происходит перерасчёт камер и слоёв
  5. сцена перерисовывается

Важно, что Controller не вызывает рендер напрямую — только инициирует обновление состояния.


Переопределение поведения Controller

Controller можно расширять через наследование базового класса:

  • переопределение handleEvent
  • кастомизация calculateNewViewState
  • изменение реакции на pointer events

Пример расширения:

import {MapController} from '@deck.gl/core';

class CustomController extends MapController {
  handleEvent(event) {
    if (event.type === 'wheel') {
      event.scale *= 0.5;
    }
    return super.handleEvent(event);
  }
}

Такая модель позволяет внедрять доменную логику взаимодействия без изменения ядра библиотеки.


Приоритеты событий и конфликтное поведение

При одновременной активности нескольких жестов Controller применяет систему приоритетов:

  1. pinch (высший приоритет)
  2. rotate
  3. pan
  4. zoom (wheel)

Это предотвращает неоднозначность интерпретации ввода, особенно на touch-устройствах.


Производительность обработки ввода

Controller оптимизирован для высокочастотных событий:

  • throttle обработки pointermove
  • requestAnimationFrame для обновления состояния
  • минимизация аллокаций объектов viewState
  • кеширование промежуточных вычислений тригонометрии

Эти механизмы позволяют поддерживать стабильную частоту обновления даже при сложных 3D-сценах.


Интеграция с кастомными View

Controller тесно связан с системой View-проекций (например, MapView, OrbitView). Каждый View определяет, как viewState интерпретируется в камеру.

Controller формирует абстрактное состояние, а View отвечает за его геометрическое представление.

Такая декомпозиция обеспечивает:

  • независимость ввода от рендеринга
  • возможность комбинирования разных View
  • гибкость при создании сложных сцен с несколькими камерами