Производительность на мобильных

Производительность Leaflet на мобильных устройствах определяется балансом между количеством отображаемых объектов, типом используемого рендерера и стоимостью операций, связанных с DOM и перерисовкой карты. Ограниченные ресурсы CPU и GPU, а также особенности браузеров на iOS и Android требуют строгого контроля над тем, что именно попадает в сцену и как часто происходит обновление отображения.

Рендеринг слоёв и влияние DOM

В Leaflet большая часть объектов карты (маркер, полилинии, полигоны) по умолчанию создаётся как DOM-элементы. Это удобно для интерактивности, но дорого на мобильных устройствах при росте количества объектов.

Каждый L.marker создаёт отдельный HTML-элемент, что приводит к:

  • увеличению нагрузки на layout и reflow
  • росту затрат на обработку touch-событий
  • увеличению времени перерисовки при панорамировании

Особенно критичны сценарии с тысячами маркеров. В таких случаях DOM становится узким местом быстрее, чем геометрия или сеть.

Canvas-рендеринг для векторных слоёв

Leaflet поддерживает альтернативный рендеринг через Canvas для векторных слоёв:

const map = L.map('map', {
  preferCanvas: true
});

Canvas снижает нагрузку на DOM, так как отрисовка выполняется в одном элементе <canvas>, а не через множество узлов. Это существенно ускоряет:

  • отображение полилиний
  • рендеринг полигонов
  • динамические обновления геометрии

Однако Canvas имеет ограничения: сложнее реализуются индивидуальные события на каждый объект, а масштабирование больших наборов данных требует дополнительных оптимизаций.

Кластеризация маркеров

Основной метод оптимизации точечных данных — агрегация через кластеризацию. При использовании leaflet.markercluster количество DOM-элементов сокращается на порядки.

Принцип работы:

  • маркеры группируются по расстоянию в пикселях
  • вместо множества элементов создаётся один кластер
  • при зуме кластеры динамически распадаются

Критически важно, что кластеризация уменьшает не только рендеринг, но и:

  • количество обработчиков событий
  • время пересчёта позиций при pan/zoom
  • нагрузку на hit-testing

При мобильных сценариях оптимально держать количество видимых элементов в пределах сотен, а не тысяч.

Оптимизация тайлов и сетевой нагрузки

Карты на Leaflet в большинстве случаев используют тайловую модель. Производительность сильно зависит от:

  • размера тайлов (256 vs 512)
  • формата изображений (PNG, WebP, JPEG)
  • кэширования HTTP
  • количества одновременно загружаемых тайлов

Снижение нагрузки достигается следующими подходами:

  • использование CDN с долгим кешированием
  • ограничение maxZoom
  • уменьшение tileSize только при необходимости
  • применение WebP для современных браузеров

Важный фактор — избегание лишних перезагрузок тайлов при частых move событиях. Leaflet уже применяет debounce внутри, но агрессивные обновления могут перегружать сеть.

Управление событиями и частотой обновлений

На мобильных устройствах события touch и gesture генерируются с высокой частотой. Обработчики move, zoom, drag могут запускаться десятки раз в секунду.

Оптимизация достигается через:

  • использование moveend вместо move
  • использование zoomend вместо zoom
  • минимизацию логики внутри on('move')
  • применение requestAnimationFrame для синхронизации визуальных обновлений

Пример сглаживания обновлений:

let scheduled = false;

map.on('move', () => {
  if (!scheduled) {
    scheduled = true;
    requestAnimationFrame(() => {
      updateUI();
      scheduled = false;
    });
  }
});

Такой подход предотвращает блокировку основного потока.

Упрощение геометрии объектов

Сложные полилинии и полигоны с большим числом точек резко ухудшают производительность. Особенно заметно это при масштабировании и вращении карты.

Оптимизация заключается в:

  • упрощении геометрии (Douglas-Peucker)
  • снижении плотности точек на низких зумах
  • динамической подгрузке детализации

Пример концепции уровней детализации:

  • zoom 0–8: грубая геометрия
  • zoom 9–12: средняя детализация
  • zoom 13+: полная детализация

Это снижает нагрузку на CPU при отрисовке.

Рендеринг иконок и изображений маркеров

Мобильные устройства чувствительны к:

  • размеру изображений маркеров
  • количеству уникальных иконок
  • теням и CSS-фильтрам

Оптимизации:

  • использование sprite-иконок вместо отдельных файлов
  • отказ от box-shadow и blur-эффектов
  • минимизация divIcon при массовом использовании
  • предзагрузка иконок

Каждая уникальная иконка увеличивает нагрузку на кеш браузера и память GPU.

Viewport-based loading (загрузка по области видимости)

Загрузка данных только в пределах текущего viewport снижает объем вычислений и рендеринга.

Принцип:

  • вычисляется bounding box карты
  • запрашиваются только объекты внутри границ
  • обновление происходит при moveend
map.on('moveend', () => {
  const bounds = map.getBounds();
  loadData(bounds);
});

Это особенно важно при работе с геоданными высокой плотности.

Ограничение количества одновременно активных слоёв

Каждый слой в Leaflet добавляет дополнительную стоимость:

  • пересчёт координат
  • участие в событийной модели
  • участие в перерисовке

Практика оптимизации:

  • удаление неиспользуемых слоёв через removeLayer
  • объединение визуально схожих слоёв
  • избегание вложенных групп без необходимости

Аппаратное ускорение и CSS-трансформации

Leaflet использует CSS transforms для панорамирования, что позволяет задействовать GPU. Однако эффективность зависит от:

  • отсутствия тяжёлых теней
  • минимального количества repaint-triggering свойств
  • корректного использования transform: translate3d

Избыточные стили могут вернуть рендеринг в CPU-режим, что резко снижает плавность.

Управление памятью и жизненным циклом объектов

На мобильных устройствах утечки памяти проявляются быстрее из-за ограниченного heap.

Типичные источники проблем:

  • не удалённые маркеры
  • подписки на события без off
  • сохранённые ссылки на map и layers

Корректное удаление:

map.removeLayer(layer);
layer.off();
layer = null;

Регулярная очистка особенно важна при динамических SPA-интерфейсах.

Баланс между интерактивностью и плотностью данных

Высокая интерактивность (hover, click, tooltip) увеличивает стоимость hit-testing. При тысячах объектов это становится узким местом.

Оптимизация:

  • ограничение интерактивности до зум-уровня
  • отключение событий на низких масштабах
  • использование делегирования событий через контейнер

Это снижает нагрузку на обработку касаний и улучшает отзывчивость интерфейса.