В Vega-Lite интерактивность строится вокруг декларативных
selection-конструкций, которые связываются с визуальными
каналами через condition. Наиболее распространённый
сценарий — highlight-паттерн, при котором один элемент
визуализации подсвечивается при наведении или выборе, а остальные
элементы остаются в приглушённом состоянии.
Ключевая идея заключается в разделении состояния данных на два слоя:
Наиболее простой вариант реализуется через selection с
типом single и on: "mouseover":
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": { "url": "data/cars.json" },
"selection": {
"highlight": {
"type": "single",
"on": "mouseover",
"empty": "none"
}
},
"mark": "point",
"encoding": {
"x": { "field": "Horsepower", "type": "quantitative" },
"y": { "field": "Miles_per_Gallon", "type": "quantitative" },
"color": {
"condition": {
"selection": "highlight",
"value": "#e45756"
},
"value": "#d3d3d3"
},
"size": {
"condition": {
"selection": "highlight",
"value": 120
},
"value": 40
}
}
}
В этой конфигурации:
selection.highlight фиксирует активную точку под
курсоромcondition.selection управляет визуальным откликомHighlight-паттерн почти всегда включает три слоя логики:
Событие (on) Определяет, как
активируется состояние:
mouseover — наведениеclick — фиксация выбораСостояние selection
single — один активный объектmulti — набор активных объектовВизуальная реакция
color)opacity)size)shape)stroke)Для аналитических интерфейсов более характерен сценарий, где
пользователь выделяет сразу несколько элементов. В Vega-Lite это
реализуется через type: "multi".
{
"data": { "url": "data/cars.json" },
"selection": {
"picked": {
"type": "multi",
"on": "click",
"toggle": "event.shiftKey"
}
},
"mark": "circle",
"encoding": {
"x": { "field": "Horsepower", "type": "quantitative" },
"y": { "field": "Acceleration", "type": "quantitative" },
"color": {
"condition": {
"selection": "picked",
"value": "#4c78a8"
},
"value": "#c0c0c0"
},
"opacity": {
"condition": {
"selection": "picked",
"value": 1
},
"value": 0.2
}
}
}
Особенности поведения:
Shift переключает режим toggleОдним из наиболее используемых подходов является не столько выделение выбранных элементов, сколько приглушение остальных.
Этот паттерн особенно полезен в плотных scatterplot или bar chart.
"opacity": {
"condition": {
"selection": "picked",
"value": 1
},
"value": 0.15
}
Семантика проста:
Этот подход лучше масштабируется, чем усиление выбранных элементов, особенно при большом количестве точек.
В Vega-Lite selection может быть привязан к легенде, что позволяет использовать легенду как интерфейс фильтрации.
{
"selection": {
"byCategory": {
"type": "multi",
"fields": ["Origin"],
"bind": "legend"
}
},
"mark": "bar",
"encoding": {
"x": { "aggregate": "count" },
"y": { "field": "Origin", "type": "nominal" },
"color": {
"condition": {
"selection": "byCategory",
"field": "Origin",
"type": "nominal"
},
"value": "lightgray"
}
}
}
Здесь легенда становится управляющим интерфейсом:
Brush selection используется для выделения диапазонов.
{
"selection": {
"brush": {
"type": "interval"
}
},
"mark": "point",
"encoding": {
"x": { "field": "Horsepower", "type": "quantitative" },
"y": { "field": "Miles_per_Gallon", "type": "quantitative" },
"opacity": {
"condition": {
"selection": "brush",
"value": 1
},
"value": 0.1
}
}
}
Поведение:
Interval selection часто комбинируется с
resolve: "global" при нескольких слоях графика.
При плотных точечных графиках курсор редко попадает точно в точку.
Для этого используется nearest: true.
"selection": {
"hover": {
"type": "single",
"on": "mouseover",
"nearest": true,
"empty": "none"
}
}
Механика:
Этот паттерн критичен для scatterplot с высокой плотностью.
В реальных интерфейсах часто используются сразу несколько selection-состояний.
Пример комбинированной модели:
"selection": {
"hover": {
"type": "single",
"on": "mouseover",
"nearest": true
},
"picked": {
"type": "multi",
"on": "click",
"toggle": "event.shiftKey"
}
}
И логика визуализации:
Когда одновременно существуют hover и multi-select, возникает задача приоритизации:
Типичный порядок:
Реализация через condition с вложенными проверками:
"color": {
"condition": [
{
"selection": "hover",
"value": "#f58518"
},
{
"selection": "picked",
"value": "#4c78a8"
}
],
"value": "#d9d9d9"
}
Такой подход позволяет строить иерархические визуальные состояния.
Selection может использоваться не только для визуального highlight, но и как фильтр для других слоёв.
"transform": [
{ "filter": { "selection": "picked" } }
]
Это превращает график в интерактивный фильтр:
В layered визуализациях (stacked + overlay) selection применяется выборочно:
{
"layer": [
{
"mark": "line",
"encoding": {
"x": { "field": "year" },
"y": { "field": "value" }
}
},
{
"mark": "point",
"selection": {
"hover": { "type": "single", "on": "mouseover", "nearest": true }
},
"encoding": {
"x": { "field": "year" },
"y": { "field": "value" },
"size": {
"condition": { "selection": "hover", "value": 120 },
"value": 30
}
}
}
]
}
Такой разделённый подход уменьшает визуальный шум и отделяет структуру от интерактивности.
В более гибких сценариях Vega позволяет использовать выражения:
"opacity": {
"condition": "datum.selected === true",
"value": 0.2
}
Или через expr:
"condition": {
"test": "datum.origin === 'USA'",
"value": "red"
}
Это даёт возможность реализовывать кастомные highlight-логики вне стандартного selection API.
При работе с тысячами и десятками тысяч точек highlight-паттерны требуют оптимизации:
nearest: true вместо точного
hit-testingОсобенно критично в canvas-renderer vs SVG:
Selection может быть глобальным объектом, используемым в нескольких view:
"resolve": "global"
Это позволяет:
Модель поведения становится связанной системой состояний, а не отдельными графиками.