SVG renderer

В Leaflet векторный рендеринг через SVG используется для отображения геометрических слоёв поверх тайловой карты с помощью DOM-элементов SVG, где каждая линия, полигон или окружность преобразуется в соответствующий SVG-path. Такой подход обеспечивает высокую чёткость на любых масштабах, поскольку графика не теряет качество при масштабировании карты.

SVG renderer в Leaflet представляет собой слой абстракции над DOM, который управляет созданием единого SVG-контейнера внутри панели карты. Все векторные объекты группируются внутри <svg> элемента, а конкретные геометрии представлены через <path>.

Основные компоненты:

  • контейнер SVG (<svg>)
  • группа слоёв (<g>)
  • path-элементы для геометрии
  • трансформации через translate и scale

Каждый векторный слой Leaflet преобразуется в SVG path, а обновление состояния карты (панорамирование, зум) вызывает перерасчёт трансформаций.

Отличие SVG и Canvas renderer

Leaflet поддерживает два основных рендера векторных данных: SVG и Canvas.

SVG:

  • каждый объект — отдельный DOM-элемент
  • высокая детализация при масштабировании
  • удобство стилизации через CSS
  • более высокая нагрузка при большом количестве объектов

Canvas:

  • рисование в одном bitmap-слое
  • лучшая производительность при тысячах объектов
  • невозможность прямого доступа к каждому объекту через DOM
  • менее гибкая стилизация

SVG renderer выбирается по умолчанию для небольших и средних наборов данных, где важна интерактивность отдельных объектов.

Инициализация SVG renderer

В Leaflet SVG renderer создаётся через L.svg():

const map = L.map('map');

const svgRenderer = L.svg();

svgRenderer.addTo(map);

Однако чаще renderer не добавляется вручную, а используется через опцию renderer.

Использование renderer в слоях

Каждый векторный слой может быть привязан к конкретному renderer:

const map = L.map('map');

const svg = L.svg();

L.circle([51.5, -0.09], {
    radius: 500,
    renderer: svg,
    color: 'red',
    weight: 2
}).addTo(map);

Если renderer не указан, Leaflet автоматически выбирает SVG или Canvas в зависимости от окружения и настроек.

Внутренние настройки L.svg

SVG renderer принимает набор опций, влияющих на поведение контейнера:

const svg = L.svg({
    padding: 0.1,
    tolerance: 3
});

Ключевые параметры:

padding

  • расширяет область отрисовки за пределы viewport
  • предотвращает «обрезание» линий на краях карты

tolerance

  • влияет на упрощение геометрии
  • используется при обработке взаимодействий

Принцип отрисовки геометрии

Leaflet преобразует географические координаты в пиксельные через проекцию карты, после чего формирует SVG path.

Например, polyline:

L.polyline([
    [51.5, -0.1],
    [51.51, -0.12],
    [51.52, -0.1]
], {
    color: 'blue',
    renderer: L.svg()
}).addTo(map);

Внутри это преобразуется в строку path вида:

M x1 y1 L x2 y2 L x3 y3

Рендеринг различных типов слоёв

SVG renderer поддерживает все основные vector layers:

Polyline

Линии отображаются через последовательность сегментов:

L.polyline(coords, {
    weight: 4,
    opacity: 0.8,
    renderer: L.svg()
});

Polygon

Полигоны формируются как замкнутые path:

L.polygon(coords, {
    fillColor: 'green',
    fillOpacity: 0.4,
    renderer: L.svg()
});

Circle и CircleMarker

Круги могут быть представлены как географические радиусы или пиксельные маркеры:

L.circle([51.5, -0.09], {
    radius: 1000,
    renderer: L.svg()
});

FeatureGroup и группировка SVG элементов

SVG renderer тесно интегрирован с FeatureGroup, позволяя управлять наборами объектов как единым слоем:

const group = L.featureGroup([
    L.circle([51.5, -0.09], { radius: 500 }),
    L.polyline([[51.49, -0.08], [51.5, -0.06]])
]).addTo(map);

Все элементы группы будут находиться внутри одного SVG контейнера, сохраняя единый жизненный цикл.

Стилизация SVG слоёв

SVG renderer использует path-стили Leaflet:

L.polyline(coords, {
    color: '#3388ff',
    weight: 5,
    opacity: 1,
    dashArray: '10, 5',
    lineCap: 'round',
    lineJoin: 'round'
});

Поддерживаемые свойства:

  • color
  • weight
  • opacity
  • fillColor
  • fillOpacity
  • dashArray
  • lineCap
  • lineJoin

Эти параметры транслируются в SVG attributes и CSS styles.

Масштабирование и трансформации

SVG renderer не перерисовывает геометрию при каждом зуме. Вместо этого используется трансформация группы <g>:

  • translate — смещение при панорамировании
  • scale — изменение масштаба

Это позволяет минимизировать перерасчёт path-данных, сохраняя производительность.

Обработка событий

Каждый SVG path остаётся DOM-элементом, поэтому поддерживаются стандартные события:

const poly = L.polygon(coords, {
    renderer: L.svg()
}).addTo(map);

poly.on('click', function () {
    console.log('Polygon clicked');
});

События:

  • click
  • mouseover
  • mouseout
  • mousedown
  • mousemove

SVG позволяет точно определять попадание курсора в конкретный объект через hit-testing браузера.

Производительность SVG renderer

SVG хорошо масштабируется до сотен и тысяч объектов, но имеет ограничения:

  • большое количество DOM-узлов замедляет рендеринг
  • сложные path увеличивают нагрузку на layout engine
  • частые обновления геометрии дорогостоящи

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

  • использование simplify() для GeoJSON
  • уменьшение количества сегментов
  • группировка через FeatureGroup
  • переключение на Canvas при больших данных

Работа с GeoJSON

SVG renderer активно используется при отображении GeoJSON:

L.geoJSON(data, {
    renderer: L.svg(),
    style: function (feature) {
        return {
            color: feature.properties.color,
            weight: 2
        };
    }
}).addTo(map);

Каждая геометрия преобразуется в SVG path, сохраняя привязку к исходным данным.

Кастомный SVG renderer

Leaflet позволяет создавать расширенные renderer на основе базового SVG:

const CustomSVG = L.SVG.extend({
    _initContainer: function () {
        L.SVG.prototype._initContainer.call(this);
        this._container.setAttribute('class', 'custom-svg-layer');
    }
});

const customRenderer = function () {
    return new CustomSVG();
};

Такой подход используется для внедрения дополнительных SVG-слоёв или кастомной логики управления DOM.

Совместное использование SVG и Canvas

В одной карте можно комбинировать разные renderer:

L.polyline(coords, { renderer: L.svg() }).addTo(map);

L.circle([51.5, -0.1], { renderer: L.canvas() }).addTo(map);

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

Поведение при изменении размера карты

SVG контейнер автоматически подстраивается под изменения viewport:

  • пересоздание размеров <svg>
  • перерасчёт bounding box
  • обновление transform группы

Это происходит синхронно с событиями resize и zoom.

Интерактивность SVG слоёв

SVG renderer обеспечивает высокую точность hit-area, поскольку каждый path — отдельный элемент DOM. Это даёт возможность:

  • точного наведения
  • подсветки отдельных сегментов
  • изменения стиля в реальном времени
  • анимации через CSS transitions

Анимации часто реализуются через изменение stroke-dashoffset или opacity.

Ограничения SVG renderer

SVG renderer сталкивается с системными ограничениями браузера:

  • деградация при десятках тысяч элементов
  • высокая стоимость reflow при частых обновлениях
  • зависимость от DOM дерева
  • нагрузка на память при сложных геометриях