Экспорт в SVG и PNG через View API

Экспорт визуализаций в Vega реализуется на уровне экземпляра View, который является рантаймом исполнения спецификации. После компиляции Vega-Lite в Vega-спецификацию весь процесс рендеринга и последующего экспорта управляется именно этим объектом.

Ключевой принцип: экспорт выполняется не из исходной спецификации, а из отрендеренного состояния View, включающего вычисленные масштабы, данные, трансформации и DOM/canvas представление.


View API как основа экспорта

View инкапсулирует:

  • загруженные и преобразованные данные
  • вычисленные шкалы (scales)
  • отрисованные сцены (scenegraph)
  • контекст рендеринга (Canvas или SVG)
  • состояние интерактивности

Создание View обычно происходит после компиляции Vega-Lite:

import vegaEmbed from 'vega-embed';

const spec = {
  data: { values: [10, 20, 30] },
  mark: "bar",
  encoding: {
    x: { field: "value", type: "quantitative" }
  }
};

const result = await vegaEmbed('#vis', spec);
const view = result.view;

Именно view предоставляет экспортные методы.


SVG-экспорт через View API

SVG-экспорт строится на сериализации scenegraph в векторное представление. Vega генерирует полноценный SVG DOM, включая:

  • <path> для геометрии
  • <text> для подписей
  • <g> для группировки элементов
  • <defs> для градиентов и маркеров

Основной метод: view.toSVG()

const svg = await view.toSVG();

Метод возвращает строку SVG, содержащую полную структуру визуализации.


Особенности SVG-рендеринга

SVG в Vega формируется не как статическая разметка, а как результат трансформации scenegraph:

1. Координатная система

  • Все элементы переводятся в абсолютные координаты
  • Применяются computed transforms (scale, translate)

2. Текстовый слой

  • Шрифты не встраиваются автоматически
  • Используется системный fallback rendering
  • Возможны расхождения между браузерами

3. Стилизация

  • CSS-инлайнинг частично сохраняется
  • Часть стилей конвертируется в атрибуты SVG

4. Ограничения

  • Canvas-based marks (например, image-heavy plots) могут быть упрощены
  • Некоторые фильтры SVG могут отличаться от Canvas-версии

Экспорт PNG через View API

PNG-экспорт выполняется через растеризацию Canvas-контекста. Vega сначала рендерит сцену в Canvas, затем извлекает bitmap.

Основной метод: view.toCanvas() и view.toImageURL()

const canvas = await view.toCanvas();

или сразу получение изображения:

const pngURL = await view.toImageURL('png');

Механизм генерации PNG

Процесс включает несколько этапов:

  1. Рендер сцены в Canvas 2D context
  2. Применение всех трансформаций (scales, projections)
  3. Отрисовка текста через Canvas API
  4. Кодирование Canvas в PNG data URL

Управление разрешением (devicePixelRatio)

Экспорт PNG чувствителен к плотности пикселей:

const canvas = await view.toCanvas({
  scaleFactor: 2
});

Повышение scaleFactor:

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

Различия SVG и PNG экспорта

Характеристика SVG PNG
Формат Векторный Растровый
Масштабирование Бесконечное Ограничено
Скорость Быстрее генерация Медленнее
Размер файла Обычно меньше Может быть больше
Совместимость Зависит от SVG viewer Универсальная

Асинхронность экспортных операций

Все методы экспорта возвращают Promise, поскольку требуют завершённого рендеринга сцены.

await view.runAsync();

const svg = await view.toSVG();
const png = await view.toImageURL('png');

Важно учитывать, что runAsync() завершает все dataflow-операции перед экспортом.


Экспорт после обновления данных

При динамическом обновлении данных требуется повторный прогон pipeline:

view.change('table', vega.changeset().insert(newData));
await view.runAsync();

const svg = await view.toSVG();

Влияние scenegraph на экспорт

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

Он включает:

  • узлы группировки
  • геометрические примитивы
  • текстовые элементы
  • интерактивные слои

SVG-экспорт сериализует scenegraph напрямую, тогда как PNG проходит через Canvas-рендеринг.


Шрифты и проблемы рендеринга текста

SVG-экспорт часто сталкивается с проблемами:

  • отсутствие встроенных шрифтов
  • различия между системами
  • fallback на sans-serif

PNG-экспорт более предсказуем, так как текст уже растеризован.


Кросс-доменные ограничения и изображения

При наличии внешних изображений:

  • Canvas может быть “tainted” при отсутствии CORS
  • PNG-экспорт может быть заблокирован браузером
  • SVG сохраняет ссылки <image href="">

Экспорт сложных визуализаций

Для многослойных спецификаций (layered charts):

  • SVG сохраняет слои как группы <g>
  • порядок рендеринга соответствует z-index
  • прозрачности сохраняются как opacity

Vega-Lite и экспорт через View

Vega-Lite не экспортирует напрямую. Перед экспортом происходит компиляция:

import { compile } from 'vega-lite';

const vgSpec = compile(vlSpec).spec;
const view = new vega.View(vega.parse(vgSpec));

После этого применяются стандартные методы:

await view.toSVG();
await view.toImageURL('png');

Управление размером изображения

Размер экспорта зависит от width и height в spec:

const spec = {
  width: 800,
  height: 400,
  mark: "line",
  data: { values: data }
};

При экспорте:

  • SVG сохраняет логические размеры
  • PNG фиксирует пиксельные размеры

Производительность экспорта

SVG:

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

PNG:

  • требует полной растеризации
  • чувствителен к объёму данных и количеству marks

Типичные сценарии использования View API экспорта

  • генерация отчётов
  • серверный рендеринг графиков
  • экспорт в PDF-пайплайны (через SVG → PDF)
  • сохранение snapshot интерактивных дашбордов

Взаимодействие с vega-embed

vega-embed предоставляет упрощённый доступ:

const result = await vegaEmbed('#vis', spec);

const svg = await result.view.toSVG();
const png = await result.view.toImageURL('png');

При этом embed автоматически управляет размерами и контейнером, но экспорт остаётся функцией View.


Тонкости сериализации SVG

SVG-строка, получаемая из toSVG(), включает:

  • XML declaration
  • namespace attributes
  • inline styles
  • embedded definitions

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


Контроль качества экспорта

При работе с экспортом учитываются:

  • корректность финального render cycle
  • завершённость dataflow
  • стабильность layout
  • отсутствие pending transitions

Экспорт выполняется только после стабилизации состояния View.