Интерактивные визуализации редко ограничиваются статичным отображением данных. При работе с временными рядами, большими массивами точек, финансовыми графиками или аналитическими панелями возникает необходимость перемещаться по данным, изменять масштаб отображения и исследовать отдельные фрагменты графика. В экосистеме Vega и Vega-Lite эти возможности реализуются через механизм привязки выделения к шкале.
В Vega-Lite подобная техника строится вокруг параметров
(params) и свойства bind: "scales", которое
автоматически синхронизирует область выделения с доменом шкал
визуализации.
Под zoom понимается изменение масштаба отображения данных. Под pan — перемещение области просмотра без изменения масштаба.
В Vega-Lite эти механики реализуются через интервальное выделение
(interval selection), привязанное к шкалам:
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"url": "data/stocks.csv"
},
"params": [
{
"name": "zoom",
"select": "interval",
"bind": "scales"
}
],
"mark": "line",
"encoding": {
"x": {
"field": "date",
"type": "temporal"
},
"y": {
"field": "price",
"type": "quantitative"
},
"color": {
"field": "symbol",
"type": "nominal"
}
}
}
Ключевой элемент:
"bind": "scales"
Именно он связывает выделение со шкалами осей.
После этого:
При использовании:
"bind": "scales"
Vega-Lite перестаёт трактовать интервальное выделение как обычный прямоугольник выбора. Вместо этого:
Фактически выделение становится механизмом управления отображаемой областью данных.
После активации привязки шкал доступны стандартные интерактивные действия.
Используется колесо мыши или жест pinch на тач-устройствах.
Пример:
Происходит через drag-перетаскивание.
Область данных сдвигается по оси.
Двойной клик:
"clear": "dblclick"
по умолчанию возвращает исходный домен шкалы.
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"width": 700,
"height": 400,
"data": {
"url": "data/sp500.csv"
},
"params": [
{
"name": "view",
"select": {
"type": "interval"
},
"bind": "scales"
}
],
"mark": {
"type": "line",
"interpolate": "monotone"
},
"encoding": {
"x": {
"field": "date",
"type": "temporal",
"title": "Дата"
},
"y": {
"field": "price",
"type": "quantitative",
"title": "Цена"
}
}
}
Такой график уже поддерживает:
Основа zoom/pan — интервальное выделение:
"select": {
"type": "interval"
}
Интервал хранит диапазоны:
{
"x": [100, 400],
"y": [20, 80]
}
При привязке к шкале эти значения становятся доменом осей.
Часто необходимо масштабировать только горизонтальную ось.
Например:
Для этого используется свойство encodings.
{
"params": [
{
"name": "zoomX",
"select": {
"type": "interval",
"encodings": ["x"]
},
"bind": "scales"
}
]
}
Теперь:
{
"params": [
{
"name": "zoomY",
"select": {
"type": "interval",
"encodings": ["y"]
},
"bind": "scales"
}
]
}
Подобный режим применяется реже, но полезен:
Стандартный режим:
"encodings": ["x", "y"]
или отсутствие encodings.
Пример:
{
"select": {
"type": "interval",
"encodings": ["x", "y"]
}
}
Vega-Lite позволяет переопределять события взаимодействия.
{
"select": {
"type": "interval",
"translate": "[mousedown, window:mouseup] > window:mousemove!"
}
}
Здесь:
mousedown начинает pan;mousemove двигает область;mouseup завершает действие.Иногда требуется разрешить только масштабирование.
{
"select": {
"type": "interval",
"translate": false
}
}
Теперь drag-перемещение отключено.
{
"select": {
"type": "interval",
"zoom": false
}
}
Панорамирование остаётся активным.
Можно задать собственное событие zoom:
{
"select": {
"type": "interval",
"zoom": "wheel![event.shiftKey]"
}
}
Теперь zoom работает только при зажатом Shift.
{
"params": [
{
"name": "custom",
"select": {
"type": "interval",
"zoom": "wheel!",
"translate":
"[mousedown[event.shiftKey], window:mouseup] > window:mousemove!"
},
"bind": "scales"
}
]
}
Особенности:
Одна из сильнейших возможностей Vega-Lite — общий zoom между несколькими представлениями.
{
"layer": [
{
"mark": "line"
},
{
"mark": "point"
}
],
"params": [
{
"name": "zoom",
"select": "interval",
"bind": "scales"
}
]
}
Все слои используют одну систему координат.
{
"vconcat": [
{
"mark": "line"
},
{
"mark": "area"
}
],
"resolve": {
"scale": {
"x": "shared"
}
}
}
Теперь zoom верхнего графика влияет на нижний.
Классический паттерн аналитических интерфейсов.
Состоит из:
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"vconcat": [
{
"width": 700,
"height": 300,
"mark": "line",
"encoding": {
"x": {
"field": "date",
"type": "temporal",
"scale": {
"domain": {
"param": "brush"
}
}
},
"y": {
"field": "price",
"type": "quantitative"
}
}
},
{
"width": 700,
"height": 80,
"params": [
{
"name": "brush",
"select": {
"type": "interval",
"encodings": ["x"]
}
}
],
"mark": "area",
"encoding": {
"x": {
"field": "date",
"type": "temporal"
},
"y": {
"field": "price",
"type": "quantitative"
}
}
}
]
}
Здесь нижний график управляет масштабом верхнего.
Важнейший механизм:
"domain": {
"param": "brush"
}
Он связывает домен шкалы с параметром выделения.
Это фундамент для:
Особенно полезен для больших наборов точек.
{
"mark": "point",
"params": [
{
"name": "zoom",
"select": "interval",
"bind": "scales"
}
]
}
При увеличении:
Zoom/pan может становиться дорогой операцией.
Основные причины:
vegaEmbed("#view", spec, {
renderer: "canvas"
});
Canvas значительно быстрее SVG при:
Например:
{
"mark": {
"type": "line",
"point": false
}
}
Точки часто становятся узким местом.
Перед отображением:
Иногда нельзя разрешать бесконечное приближение.
В Vega это контролируется через сигналы и ограничения домена.
В Vega-Lite прямого API меньше, но ограничения можно реализовать через:
Vega-Lite покрывает большинство сценариев, но сложные случаи требуют Vega.
Например:
В Vega масштабирование строится через сигналы
(signals).
Пример:
{
"signals": [
{
"name": "xDomain",
"update": "domain('xscale')"
}
]
}
Сигналы:
{
"name": "zoom",
"on": [
{
"events": "wheel!",
"update": "pow(1.001, event.deltaY)"
}
]
}
Так вычисляется коэффициент масштабирования.
{
"name": "delta",
"on": [
{
"events": "[mousedown, window:mouseup] > window:mousemove",
"update": "x() - xdown"
}
]
}
{
"scales": [
{
"name": "xscale",
"domain": {
"signal": "xDomain"
}
}
]
}
| Возможность | Vega-Lite | Vega |
|---|---|---|
| Базовый zoom | Да | Да |
| Pan | Да | Да |
| bind: scales | Да | Нет |
| Полный контроль событий | Ограниченно | Полностью |
| Кастомная физика | Нет | Да |
| Multi-touch логика | Частично | Полностью |
| Сложные сигналы | Нет | Да |
| Простота разработки | Очень высокая | Средняя |
Подход особенно эффективен для:
Ограничения Vega-Lite становятся заметны при:
В таких случаях применяется Vega.
Zoom может работать вместе с transform filter.
Пример:
{
"transform": [
{
"filter": "datum.price > 100"
}
]
}
После фильтрации шкалы продолжают масштабироваться.
Tooltip автоматически адаптируется к новой области просмотра.
{
"mark": {
"type": "line",
"tooltip": true
}
}
При сильном приближении tooltip становится значительно полезнее.
Наиболее частый сценарий.
{
"x": {
"field": "date",
"type": "temporal"
}
}
При zoom Vega-Lite автоматически:
При разных уровнях масштаба:
| Масштаб | Формат |
|---|---|
| Годы | 2020 |
| Месяцы | Jan 2020 |
| Дни | 12 Jan |
| Часы | 12:30 |
| Секунды | 12:30:15 |
Это делает навигацию особенно удобной.
Типичная архитектура:
Zoom становится центральным механизмом исследования данных.
Преимущества:
Недостатки:
Преимущества:
Недостатки:
Наиболее распространённая структура:
{
"params": [
{
"name": "zoom",
"select": {
"type": "interval",
"encodings": ["x"]
},
"bind": "scales"
}
]
}
Почему используется только x:
Ошибка:
"scale": {
"domain": [0, 100]
}
при активном:
"bind": "scales"
Фиксированный домен мешает интерактивному zoom.
Категориальные шкалы плохо подходят для zoom/pan.
Проблемный пример:
"type": "ordinal"
Лучше использовать:
При миллионах точек даже canvas начинает тормозить.
Решения:
Стандартное подключение:
vegaEmbed("#chart", spec);
С canvas:
vegaEmbed("#chart", spec, {
renderer: "canvas"
});
При zoom Vega/Vega-Lite автоматически:
Именно реактивная архитектура делает zoom/pan в Vega-Lite чрезвычайно компактным по коду.
Внутренне механизм работает как реактивная цепочка:
input event
↓
selection update
↓
scale domain update
↓
axis recomputation
↓
mark recomputation
↓
rerender
Эта модель лежит в основе всей интерактивности Vega и Vega-Lite.