В экосистеме Leaflet пользовательские слои представляют собой
расширяемый механизм, позволяющий интегрировать произвольные визуальные
и интерактивные элементы в карту поверх базовой тайловой подложки.
Архитектура слоёв построена вокруг абстракции L.Layer,
которая определяет единый жизненный цикл: добавление на карту,
обновление состояния и удаление из DOM или Canvas.
Каждый слой в Leaflet наследуется от базового класса
L.Layer и реализует два ключевых метода:
onAdd(map) — вызывается при добавлении слоя на
картуonRemove(map) — вызывается при удалении слояДополнительно может использоваться метод getPane() для
выбора контейнера рендеринга.
L.CustomLayer = L.Layer.extend({
onAdd: function (map) {
this._map = map;
this._container = L.DomUtil.create('div', 'custom-layer');
map.getPanes().overlayPane.appendChild(this._container);
this._update();
map.on('zoom viewreset move', this._update, this);
},
onRemove: function (map) {
L.DomUtil.remove(this._container);
map.off('zoom viewreset move', this._update, this);
},
_update: function () {
// логика перерисовки слоя
}
});
Такой подход позволяет интегрировать любые DOM- или Canvas-основанные визуализации.
Система панелей определяет, в каком слое DOM будет размещён пользовательский элемент. Основные панели:
tilePane — тайлыoverlayPane — векторные слоиmarkerPane — маркерыpopupPane — всплывающие окнаПользовательский слой чаще всего добавляется в
overlayPane, что обеспечивает корректное наложение поверх
тайлов, но ниже маркеров.
this._container = L.DomUtil.create('canvas', 'custom-canvas-layer');
map.getPanes().overlayPane.appendChild(this._container);
При необходимости можно создавать собственные панели:
map.createPane('customPane');
map.getPane('customPane').style.zIndex = 650;
Для синхронизации слоя с картой используется преобразование
географических координат в пиксельные через методы
latLngToLayerPoint и layerPointToLatLng.
_update: function () {
const topLeft = this._map.containerPointToLayerPoint([0, 0]);
L.DomUtil.setPosition(this._container, topLeft);
this._draw();
},
_draw: function () {
const point = this._map.latLngToLayerPoint([55.75, 37.61]);
this._ctx.fillStyle = 'red';
this._ctx.beginPath();
this._ctx.arc(point.x, point.y, 5, 0, Math.PI * 2);
this._ctx.fill();
}
Этот механизм обеспечивает корректную работу при масштабировании и перемещении карты.
Canvas-слои применяются для визуализации большого количества геометрии. Они обеспечивают высокую производительность по сравнению с DOM-элементами.
L.CanvasLayer = L.Layer.extend({
onAdd: function (map) {
this._canvas = L.DomUtil.create('canvas', 'canvas-layer');
this._ctx = this._canvas.getContext('2d');
map.getPanes().overlayPane.appendChild(this._canvas);
this._resize();
map.on('resize zoom move', this._redraw, this);
},
_resize: function () {
const size = this._map.getSize();
this._canvas.width = size.x;
this._canvas.height = size.y;
},
_redraw: function () {
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
}
});
Canvas-слои часто применяются для тепловых карт, плотностей и динамических визуализаций.
SVG-слои удобны для интерактивных векторных объектов, так как позволяют работать с DOM-событиями.
L.SVGLayer = L.Layer.extend({
onAdd: function (map) {
this._svg = L.SVG.create('svg');
map.getPanes().overlayPane.appendChild(this._svg);
this._group = L.SVG.create('g');
this._svg.appendChild(this._group);
map.on('zoom move', this._update, this);
}
});
SVG предпочтителен при небольшом количестве объектов, где важна интерактивность и стилизация через CSS.
Механизм L.GeoJSON позволяет расширять поведение слоёв
через параметры:
pointToLayer — кастомизация точечных объектовstyle — стилизация геометрииonEachFeature — обработка событийL.geoJSON(data, {
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng, {
radius: 6,
color: 'blue'
});
},
onEachFeature: function (feature, layer) {
layer.on('click', function () {
layer.bindPopup(feature.properties.name);
});
}
});
Этот механизм фактически является высокоуровневой формой пользовательских слоёв.
Пользовательские слои часто строятся поверх встроенных реализаций:
L.ImageOverlay — наложение изображения на координатный
прямоугольникL.VideoOverlay — привязка видео к географической
областиconst overlay = L.imageOverlay('map.png', [
[55.7, 37.5],
[55.8, 37.7]
]).addTo(map);
Эти слои уже содержат внутреннюю реализацию синхронизации координат и обновления.
Слой может реагировать на события карты:
zoomstart, zoomendmove, moveendresizethis._map.on({
zoom: this._update,
move: this._update,
resize: this._resize
}, this);
Это позволяет реализовывать динамическое обновление данных и адаптивную отрисовку.
Ключевые аспекты оптимизации:
move и
zoomrequestAnimationFrame для
перерисовки_update: function () {
if (this._animating) return;
this._animating = true;
requestAnimationFrame(() => {
this._animating = false;
this._render();
});
}
Пользовательские слои часто строятся как композиции:
const CompositeLayer = L.Layer.extend({
onAdd: function (map) {
this._subLayer = new L.CustomLayer();
map.addLayer(this._subLayer);
}
});
Такой подход позволяет разделять ответственность и упрощать поддержку сложных визуализаций.