Класс Icon

В библиотеке Leaflet иконки маркеров реализуются через систему классов L.Icon и L.DivIcon, где базовый класс L.Icon отвечает за отображение растровых изображений маркеров и их поведения на карте. Архитектура класса построена вокруг описания ресурсов изображения и параметров позиционирования, что позволяет гибко управлять тем, как маркер визуально привязан к координате на карте.

Базовая роль L.Icon

L.Icon определяет внешний вид маркера через набор параметров:

  • основной файл изображения
  • размер иконки
  • точку привязки к координате
  • смещение всплывающих окон
  • изображение тени (опционально)
  • параметры для экранов с высокой плотностью пикселей

Экземпляр L.Icon не занимается логикой карты или координатами, он исключительно описывает визуальное представление маркера.

Создание иконки осуществляется через конструктор:

const icon = L.icon(options);

где options — объект конфигурации.

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

iconUrl

Параметр iconUrl задаёт путь к изображению маркера.

const icon = L.icon({
  iconUrl: 'marker.png'
});

Это обязательное поле, без которого иконка не может быть корректно отрисована (если не используется кастомная реализация).


iconSize

iconSize определяет размеры изображения в пикселях.

iconSize: [32, 32]

Размер влияет на:

  • область отрисовки изображения
  • позиционирование относительно точки на карте
  • вычисление anchor-точек

Важно, что Leaflet не масштабирует изображение автоматически под контейнер карты, поэтому размер должен соответствовать фактическому разрешению ресурса или быть явно адаптирован.


iconAnchor

iconAnchor задаёт точку внутри изображения, которая будет привязана к географической координате маркера.

iconAnchor: [16, 32]

Если иконка размером 32×32, то точка [16, 32] означает нижний центр изображения.

Без правильного iconAnchor маркеры визуально «прыгают» относительно координаты, особенно при использовании нестандартных иконок.


popupAnchor

popupAnchor определяет смещение всплывающего окна относительно точки привязки иконки.

popupAnchor: [0, -32]

Обычно используется для того, чтобы popup появлялся над маркером, а не перекрывал его.

Логика смещения всегда строится от iconAnchor.


shadowUrl и shadowSize

Тень маркера задаётся отдельно:

shadowUrl: 'marker-shadow.png',
shadowSize: [41, 41]

Тень рисуется под основной иконкой и также требует корректной синхронизации размеров. Несовпадение размеров тени и иконки часто приводит к визуальным артефактам.


shadowAnchor

Аналогично iconAnchor, определяет точку привязки тени:

shadowAnchor: [12, 41]

Это позволяет выровнять тень относительно основного маркера.


className

Позволяет добавить CSS-класс к контейнеру иконки:

className: 'custom-marker'

Используется для:

  • кастомной стилизации
  • анимаций через CSS
  • управления состояниями маркеров

Полный пример конфигурации

const customIcon = L.icon({
  iconUrl: 'marker.png',
  shadowUrl: 'marker-shadow.png',
  iconSize: [32, 32],
  shadowSize: [41, 41],
  iconAnchor: [16, 32],
  shadowAnchor: [12, 41],
  popupAnchor: [0, -32],
  className: 'custom-marker'
});

Применение иконки к маркеру

Иконка передаётся в объект маркера через параметр icon:

L.marker([51.5, -0.09], { icon: customIcon }).addTo(map);

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


Внутреннее устройство L.Icon

L.Icon реализует два ключевых метода:

  • createIcon
  • createShadow

Эти методы возвращают DOM-элементы, которые добавляются в карту.

createIcon

Создаёт основной элемент изображения:

  • создаётся <img> или <div> (в зависимости от реализации)
  • устанавливается src = iconUrl
  • применяются размеры через CSS
  • добавляются классы

Упрощённая логика:

createIcon() {
  const img = document.createElement('img');
  img.src = this.options.iconUrl;
  img.style.width = this.options.iconSize[0] + 'px';
  img.style.height = this.options.iconSize[1] + 'px';
  return img;
}

createShadow

Работает аналогично createIcon, но создаёт отдельный слой тени:

createShadow() {
  const img = document.createElement('img');
  img.src = this.options.shadowUrl;
  return img;
}

Если shadowUrl не задан, метод может возвращать null.


Переопределение L.Icon

L.Icon можно расширять для создания кастомных классов:

const MyIcon = L.Icon.extend({
  options: {
    iconUrl: 'custom.png',
    iconSize: [40, 40],
    iconAnchor: [20, 40]
  }
});

Использование:

const icon = new MyIcon();

Такой подход полезен при создании набора стандартизированных иконок для разных типов объектов (магазины, остановки, события).


Retina-иконки

Поддержка экранов с высокой плотностью пикселей реализуется через параметр iconRetinaUrl:

const icon = L.icon({
  iconUrl: 'marker.png',
  iconRetinaUrl: 'marker@2x.png',
  iconSize: [32, 32]
});

При обнаружении retina-дисплея библиотека автоматически подменяет изображение.


Поведение при отсутствии параметров

Если не указать icon, используется L.Icon.Default, который:

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

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


Взаимодействие с CSS

Иконка маркера является DOM-элементом, поэтому её поведение частично контролируется через CSS:

.custom-marker {
  transition: transform 0.2s ease;
}

Это позволяет реализовать:

  • анимацию появления
  • масштабирование при hover
  • изменение прозрачности
  • эффекты активности

Важно учитывать, что позиционирование маркера управляется Leaflet, поэтому нельзя произвольно изменять position без риска нарушения привязки.


Производительность и оптимизация

При работе с большим количеством маркеров важно учитывать:

  • каждый L.Icon создаёт отдельный DOM-узел
  • использование сложных изображений увеличивает нагрузку
  • shadow-иконки удваивают количество элементов

Оптимизационные подходы:

  • использование одинаковых экземпляров L.Icon
  • применение DivIcon вместо изображений при массовых объектах
  • отказ от теней в больших слоях данных

Различие L.Icon и L.DivIcon

Хотя основная тема — L.Icon, важно учитывать альтернативу:

  • L.Icon — изображения (<img>)
  • L.DivIcon — HTML-элементы (<div>)

L.DivIcon используется для:

  • текстовых маркеров
  • динамических значений
  • сложных HTML-структур

L.Icon предпочтителен для:

  • стандартных картографических маркеров
  • производительных слоёв
  • простых визуальных индикаторов

Типичные ошибки при использовании

  1. Неверный iconAnchor

    • маркер смещается относительно координаты
  2. Отсутствие iconSize

    • приводит к некорректному позиционированию
  3. Использование изображений без оптимизации

    • ухудшает производительность
  4. Несоответствие shadow размерам

    • визуальные артефакты
  5. Динамическая смена iconUrl без обновления маркера

    • требуется пересоздание иконки или маркера

Динамическая смена иконки

Иконку можно изменить после создания маркера:

marker.setIcon(newIcon);

При этом Leaflet пересоздаёт DOM-элемент и обновляет отображение.

Такой подход используется для:

  • изменения состояния объекта
  • отображения активности
  • визуального переключения типов маркеров

Кастомизация через фабрики иконок

В сложных приложениях часто используется фабрика:

function createIcon(type) {
  return L.icon({
    iconUrl: `${type}.png`,
    iconSize: [32, 32],
    iconAnchor: [16, 32]
  });
}

Это упрощает управление наборами иконок и уменьшает дублирование конфигураций.


Итоговая модель поведения L.Icon

L.Icon представляет собой слой абстракции над DOM-элементом изображения, где:

  • визуальное представление отделено от координат
  • позиционирование определяется anchor-точками
  • ресурсы загружаются как статические файлы
  • взаимодействие осуществляется через маркеры Leaflet

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