В Leaflet отрисовка векторных слоёв традиционно выполняется через SVG, однако для сценариев с большим количеством объектов или высокой динамикой обновления используется Canvas-рендерер, позволяющий существенно снизить нагрузку на DOM и ускорить перерисовку сцены.
Canvas-рендерер в Leaflet реализован через класс
L.Canvas, который наследуется от базового
L.Renderer и предоставляет API для отрисовки геометрий на
HTML5 <canvas> вместо SVG-узлов. Основное отличие
заключается в том, что вся графика рисуется в одном растровом контексте,
а не как набор отдельных DOM-элементов.
Внутри Leaflet рендерер Canvas работает как слой абстракции между
векторными объектами (L.Path и его наследниками) и HTML5
Canvas API.
Каждый векторный слой (Polyline, Polygon, Circle, Rectangle) при
выборе Canvas-рендерера передаёт геометрию в L.Canvas,
который преобразует координаты географической системы в пиксельные и
выполняет отрисовку через CanvasRenderingContext2D.
Ключевые элементы архитектуры:
L.Canvas — основной рендерерL.Renderer — базовый абстрактный классL.Path — базовый класс для векторных слоёвrenderer._ctx — 2D контекст canvasrenderer._update() — перерисовка сценыCanvas-рендерер можно создать явно и передать в слой карты:
const canvasRenderer = L.canvas();
const map = L.map('map', {
center: [55.75, 37.61],
zoom: 10
});
После этого рендерер применяется к слоям через опцию
renderer:
const polyline = L.polyline([
[55.75, 37.61],
[55.80, 37.70],
[55.85, 37.65]
], {
renderer: canvasRenderer,
color: 'blue',
weight: 3
}).addTo(map);
Если рендерер не указан явно, Leaflet может автоматически выбрать Canvas при определённых условиях, но в большинстве случаев выбор задаётся вручную.
Разница между Canvas и SVG-рендерерами критична при проектировании карт с высокой нагрузкой.
Canvas становится предпочтительным при:
В отличие от SVG, где каждый update может приводить к перерасчёту DOM-дерева, Canvas выполняет перерисовку единого буфера.
Внутренний цикл выглядит упрощённо следующим образом:
clearRect)Canvas-рендерер поддерживает набор опций, влияющих на качество и поведение отрисовки:
const renderer = L.canvas({
padding: 0.5,
tolerance: 10
});
Определяет дополнительную область вокруг viewport, в которой заранее отрисовываются объекты.
Используется для определения чувствительности при hit-testing (определение попадания курсора в линию).
Canvas-рендерер работает с любыми наследниками
L.Path:
L.PolylineL.PolygonL.CircleL.RectangleL.GeoJSON (векторная часть)Пример отрисовки GeoJSON через Canvas:
L.geoJSON(data, {
renderer: L.canvas(),
style: function (feature) {
return {
color: '#3388ff',
weight: 2,
fillOpacity: 0.3
};
}
}).addTo(map);
Canvas не хранит отдельные DOM-узлы для каждого объекта, поэтому обновление состояния происходит через централизованный механизм.
Основные этапы:
layer.redraw() — принудительная перерисовка слояmap.on('move zoom', ...) — триггер обновления
сценыrenderer._redraw() — внутренний вызов Canvas очистки и
отрисовкиПри изменении стиля или геометрии слой помечается как «dirty» и включается в следующий цикл рендера.
Canvas не может назначать события на каждый объект напрямую, поэтому используется геометрическое определение попадания точки.
Leaflet реализует это через:
Пример обработки клика:
const layer = L.polyline(coords, {
renderer: L.canvas()
}).addTo(map);
layer.on('click', function (e) {
console.log('Клик по линии', e.latlng);
});
Несмотря на отсутствие DOM-узлов, события работают прозрачно для разработчика.
Canvas использует методы контекста:
ctx.beginPath()ctx.moveTo()ctx.lineTo()ctx.stroke()Каждый L.Path преобразуется в последовательность этих
команд.
Параметры стиля:
color → strokeStyleweight → lineWidthopacity → globalAlphafillColor → fillStyleCanvas-рендерер автоматически адаптируется под устройства с высокой плотностью пикселей.
Механизм включает:
devicePixelRatioЭто предотвращает размытость линий на Retina-дисплеях и аналогичных экранах.
Несмотря на высокую производительность, Canvas имеет ряд ограничений:
При этом для аналитических карт и визуализации больших данных эти ограничения компенсируются производительностью.
Leaflet допускает смешанный подход:
Типичный сценарий:
Это позволяет балансировать между производительностью и удобством взаимодействия.
Canvas-рендерер включает несколько оптимизаций:
При большом количестве объектов ключевым фактором становится
уменьшение числа вызовов ctx.stroke() и
ctx.fill().
Перед отрисовкой Leaflet выполняет преобразование координат:
Упрощённая модель:
pixel = map.project(latlng, zoom)
Canvas затем работает только с пиксельными координатами, не оперируя географическими значениями.
Canvas-рендерер применяется в системах:
В таких задачах количество объектов может достигать десятков и сотен тысяч, что делает SVG непригодным.
При изменении zoom:
Процесс оптимизируется за счёт ограничения области отрисовки текущим viewport.
Canvas-слои могут использовать функции для вычисления стилей:
function style(feature) {
return {
color: feature.properties.value > 50 ? 'red' : 'green',
weight: 2
};
}
Это позволяет реализовывать тепловые карты и тематическое картографирование без смены рендерера.
Canvas в Leaflet функционирует как высокопроизводительный растровый слой, объединяющий множество векторных объектов в единый графический буфер, с управляемым жизненным циклом, оптимизированной перерисовкой и абстрактной системой взаимодействия с событиями, скрывающей отсутствие DOM-структуры.