В Leaflet отображение маркеров основано на классе
L.Icon, который определяет внешний вид точки на карте через
изображение или HTML-разметку. Стандартная иконка, используемая
библиотекой, представляет собой набор из трёх изображений: основного
маркера, тени и изображения для Retina-дисплеев. Управление этими
элементами позволяет полностью переопределять визуальное представление
объектов на карте.
Класс L.Icon описывает набор параметров, которые влияют
на отрисовку маркера:
iconUrl — основной графический файл иконкиshadowUrl — изображение тениiconSize — размеры иконки в пикселяхshadowSize — размеры тениiconAnchor — точка привязки иконки к координатеpopupAnchor — смещение всплывающего окна относительно
иконкиclassName — CSS-класс для стилизацииПростейшее создание иконки выглядит следующим образом:
const customIcon = L.icon({
iconUrl: 'marker.png',
shadowUrl: 'shadow.png',
iconSize: [32, 32],
iconAnchor: [16, 32],
popupAnchor: [0, -32]
});
Каждый параметр влияет на точность позиционирования и визуальную
согласованность элементов на карте. Ошибки в iconAnchor
часто приводят к смещению маркера относительно координаты.
После создания иконки она передаётся в объект маркера:
const marker = L.marker([51.505, -0.09], {
icon: customIcon
}).addTo(map);
Маркер полностью наследует визуальные свойства иконки, но сохраняет географическую привязку через координаты.
Ключевое значение для точного позиционирования имеет
iconAnchor. Оно определяет, какая точка изображения будет
соответствовать координате на карте.
Например, если иконка имеет размер 32×32 пикселя:
[16, 32] — нижний центр (типичный вариант для «капли»
маркера)[0, 0] — верхний левый угол[16, 16] — центр изображенияpopupAnchor управляет расположением всплывающего
окна:
popupAnchor: [0, -32]
Это смещает popup вверх относительно точки маркера, предотвращая перекрытие.
В реальных приложениях часто требуется отображать различные категории
объектов с уникальными иконками. Для этого создаётся несколько
экземпляров L.Icon:
const restaurantIcon = L.icon({
iconUrl: 'restaurant.png',
iconSize: [30, 30],
iconAnchor: [15, 30]
});
const hotelIcon = L.icon({
iconUrl: 'hotel.png',
iconSize: [30, 30],
iconAnchor: [15, 30]
});
Далее выбор иконки определяется логикой приложения:
function getIcon(type) {
switch (type) {
case 'restaurant':
return restaurantIcon;
case 'hotel':
return hotelIcon;
}
}
Leaflet поддерживает высокоплотные дисплеи через параметр
iconRetinaUrl. Он используется автоматически, если
устройство имеет соответствующий коэффициент пикселей.
const retinaIcon = L.icon({
iconUrl: 'marker.png',
iconRetinaUrl: 'marker@2x.png',
iconSize: [32, 32],
iconAnchor: [16, 32]
});
Это позволяет сохранять чёткость изображения на современных экранах без дополнительных изменений логики.
Помимо изображений, Leaflet поддерживает HTML-иконки через
L.divIcon. Такой подход позволяет создавать динамические
иконки, стилизуемые через CSS.
const divIcon = L.divIcon({
className: 'custom-div-icon',
html: '<div class="marker-circle"></div>',
iconSize: [20, 20],
iconAnchor: [10, 10]
});
CSS:
.custom-div-icon .marker-circle {
width: 20px;
height: 20px;
background-color: red;
border-radius: 50%;
border: 2px solid white;
}
Преимущество такого подхода заключается в возможности использовать анимации, градиенты и любые CSS-эффекты без необходимости создавать изображения.
L.divIcon позволяет генерировать содержимое
программно:
function createNumberIcon(number) {
return L.divIcon({
html: `<div class="number-marker">${number}</div>`,
className: 'number-icon',
iconSize: [30, 30],
iconAnchor: [15, 15]
});
}
Такой подход используется для кластеризации, отображения счётчиков или состояния объектов.
Параметр className добавляет CSS-класс к контейнеру
иконки:
L.divIcon({
className: 'my-custom-marker'
});
Это позволяет управлять внешним видом через CSS без изменения JavaScript-логики:
.my-custom-marker {
background: transparent;
border: none;
}
Особенно полезно при интеграции с дизайн-системами или UI-фреймворками.
Для различных масштабов карты часто требуется изменять размер иконки. Это можно реализовать через обработку событий zoom:
map.on('zoomend', function () {
const zoom = map.getZoom();
const size = zoom > 10 ? 40 : 25;
marker.setIcon(L.icon({
iconUrl: 'marker.png',
iconSize: [size, size],
iconAnchor: [size / 2, size]
}));
});
Такой подход позволяет визуально выделять объекты при приближении карты.
SVG обеспечивает масштабируемость без потери качества. Его можно
использовать как через iconUrl, так и через
divIcon.
Пример с внешним SVG-файлом:
L.icon({
iconUrl: 'marker.svg',
iconSize: [32, 32],
iconAnchor: [16, 32]
});
Вариант с inline SVG:
L.divIcon({
html: `
<svg width="24" height="24" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" fill="blue"/>
</svg>
`,
className: '',
iconSize: [24, 24],
iconAnchor: [12, 12]
});
SVG особенно полезен для сложных иконок с несколькими слоями.
Создание множества одинаковых объектов L.icon может быть
неэффективным. Обычно иконки создаются один раз и переиспользуются:
const defaultIcon = L.icon({
iconUrl: 'marker.png',
iconSize: [32, 32],
iconAnchor: [16, 32]
});
И затем применяется ко многим маркерам:
L.marker([lat, lng], { icon: defaultIcon }).addTo(map);
Это снижает нагрузку на память и ускоряет рендеринг.
Leaflet позволяет динамически менять иконку уже созданного маркера:
marker.setIcon(newIcon);
Такой механизм используется для отображения состояний объектов: активен, выбран, недоступен.
Неверное позиционирование почти всегда связано с неправильным
iconAnchor. При использовании нестандартных изображений
важно учитывать реальный центр «точки привязки».
Другая проблема — отсутствие изображения при сборке проекта. В этом случае пути к файлам должны быть корректно настроены относительно итогового бандла.
Также часто возникает несоответствие размеров изображения и параметра
iconSize, что приводит к размытию или обрезанию иконки.
Иконки в Leaflet часто связаны с событиями:
marker.on('click', function () {
this.setIcon(activeIcon);
});
Это позволяет визуально отражать взаимодействие пользователя с объектами карты без изменения структуры данных.