Тип графического примитива trail предназначен для
построения линий с переменной толщиной вдоль траектории. В отличие от
обычной линии (line), где толщина остаётся постоянной,
trail позволяет кодировать дополнительную количественную
переменную через ширину сегментов.
Наиболее распространённые сценарии применения:
Основное отличие:
| Тип | Толщина |
|---|---|
line |
фиксированная |
trail |
изменяется вдоль линии |
Минимальная структура:
{
"mark": "trail",
"encoding": {
"x": {"field": "x"},
"y": {"field": "y"},
"size": {"field": "value"}
}
}
Канал size управляет толщиной линии.
vegaEmbed('#view', {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": [
{"x": 0, "y": 10, "v": 2},
{"x": 1, "y": 20, "v": 5},
{"x": 2, "y": 15, "v": 8},
{"x": 3, "y": 25, "v": 3},
{"x": 4, "y": 18, "v": 6}
]
},
"mark": "trail",
"encoding": {
"x": {"field": "x", "type": "quantitative"},
"y": {"field": "y", "type": "quantitative"},
"size": {
"field": "v",
"type": "quantitative"
}
}
});
В этом примере:
x и
y;v.Vega-Lite интерполирует толщину между соседними точками.
Например:
| Точка | size |
|---|---|
| A | 2 |
| B | 10 |
Сегмент между A и B будет плавно расширяться.
Это создаёт эффект:
По умолчанию Vega-Lite автоматически вычисляет диапазон размеров. Для
контроля используется scale.
"size": {
"field": "v",
"type": "quantitative",
"scale": {
"range": [1, 20]
}
}
Теперь:
1;20.trail поддерживает одновременное кодирование нескольких
измерений.
vegaEmbed('#view', {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": [
{"t": 0, "temp": 10, "speed": 2},
{"t": 1, "temp": 15, "speed": 4},
{"t": 2, "temp": 22, "speed": 8},
{"t": 3, "temp": 18, "speed": 5},
{"t": 4, "temp": 25, "speed": 10}
]
},
"mark": "trail",
"encoding": {
"x": {"field": "t", "type": "quantitative"},
"y": {
"field": "temp",
"type": "quantitative"
},
"size": {
"field": "speed",
"type": "quantitative"
},
"color": {
"field": "temp",
"type": "quantitative",
"scale": {
"scheme": "viridis"
}
}
}
});
Здесь:
Поддерживаются различные типы сглаживания.
"mark": {
"type": "trail",
"interpolate": "linear"
}
Стандартная ломаная линия.
"mark": {
"type": "trail",
"interpolate": "monotone"
}
Плавная кривая без резких перегибов.
"mark": {
"type": "trail",
"interpolate": "basis"
}
Максимально сглаженная кривая.
"mark": {
"type": "trail",
"interpolate": "cardinal"
}
Сглаживание с контролем формы изгиба.
vegaEmbed('#view', {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"width": 600,
"height": 300,
"data": {
"values": [
{"x": 0, "y": 10, "s": 1},
{"x": 1, "y": 25, "s": 5},
{"x": 2, "y": 12, "s": 10},
{"x": 3, "y": 30, "s": 3},
{"x": 4, "y": 20, "s": 8}
]
},
"mark": {
"type": "trail",
"interpolate": "monotone"
},
"encoding": {
"x": {"field": "x", "type": "quantitative"},
"y": {"field": "y", "type": "quantitative"},
"size": {
"field": "s",
"type": "quantitative",
"scale": {
"range": [2, 25]
}
},
"color": {
"value": "#4682b4"
}
}
});
Толстые линии могут перекрывать данные. Для этого используется
opacity.
"mark": {
"type": "trail",
"opacity": 0.6
}
Полезно при:
Для нескольких независимых траекторий используется поле категории.
"detail": {
"field": "group"
}
Пример:
vegaEmbed('#view', {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": [
{"g":"A","x":0,"y":5,"s":2},
{"g":"A","x":1,"y":8,"s":5},
{"g":"A","x":2,"y":6,"s":3},
{"g":"B","x":0,"y":10,"s":4},
{"g":"B","x":1,"y":12,"s":8},
{"g":"B","x":2,"y":9,"s":2}
]
},
"mark": "trail",
"encoding": {
"x": {"field":"x","type":"quantitative"},
"y": {"field":"y","type":"quantitative"},
"size": {
"field":"s",
"type":"quantitative"
},
"color": {
"field":"g",
"type":"nominal"
},
"detail": {
"field":"g"
}
}
});
Trail чувствителен к порядку точек.
Если данные идут не по порядку, линия будет строиться хаотично.
Используется:
"order": {
"field": "time"
}
Пример:
"encoding": {
"order": {
"field": "timestamp"
}
}
Trail особенно эффективен для временных данных.
vegaEmbed('#view', {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": [
{"date":"2025-01-01","value":20,"load":1},
{"date":"2025-01-02","value":25,"load":3},
{"date":"2025-01-03","value":15,"load":8},
{"date":"2025-01-04","value":30,"load":5}
]
},
"mark": "trail",
"encoding": {
"x": {
"field":"date",
"type":"temporal"
},
"y": {
"field":"value",
"type":"quantitative"
},
"size": {
"field":"load",
"type":"quantitative"
}
}
});
В Vega trail описывается более низкоуровнево.
{
"type": "trail",
"from": {"data": "table"},
"encode": {
"enter": {
"x": {"scale": "x", "field": "x"},
"y": {"scale": "y", "field": "y"},
"stroke": {"value": "steelblue"}
},
"update": {
"strokeWidth": {
"field": "size"
}
}
}
}
В Vega:
| Возможность | Vega-Lite | Vega |
|---|---|---|
| Простота | высокая | средняя |
| Автоматические scale | есть | нет |
| Низкоуровневый контроль | ограничен | полный |
| Быстрая разработка | отличная | хуже |
| Кастомные вычисления | ограничены | мощные |
Trail часто используется для отображения маршрутов.
"longitude": {"field": "lon"},
"latitude": {"field": "lat"},
"size": {"field": "traffic"}
Толщина может показывать:
Vega напрямую не содержит полноценной системы анимаций уровня D3, однако trail хорошо сочетается с:
Можно динамически изменять:
Trail часто комбинируется с преобразованиями.
"transform": [
{
"calculate": "datum.value * 2",
"as": "width"
}
]
{
"filter": "datum.value > 10"
}
{
"window": [
{
"op": "mean",
"field": "value",
"as": "avg"
}
]
}
Пример с hover.
vegaEmbed('#view', {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": [
{"x":0,"y":10,"s":2},
{"x":1,"y":20,"s":8},
{"x":2,"y":15,"s":5}
]
},
"params": [
{
"name": "hover",
"select": {
"type": "point",
"nearest": true,
"on": "mouseover"
}
}
],
"mark": "trail",
"encoding": {
"x": {"field":"x","type":"quantitative"},
"y": {"field":"y","type":"quantitative"},
"size": {"field":"s"},
"opacity": {
"condition": {
"param": "hover",
"value": 1
},
"value": 0.3
}
}
});
Trail более ресурсоёмок, чем обычный line.
Причины:
Особенно заметно при:
"transform": [
{
"sample": 1000
}
]
Слишком толстые линии:
Практически безопасный диапазон:
"range": [1, 20]
Наиболее быстрый вариант:
"interpolate": "linear"
"mark": "trail"
Без size trail визуально почти не отличается от обычной
линии.
"order": {
"field": "date"
}
Без сортировки возникают самопересечения.
"range": [1, 100]
График становится нечитаемым.
"size": {
"field": "category",
"type": "nominal"
}
Trail лучше работает с количественными величинами.
| Свойство | trail | area |
|---|---|---|
| Переменная толщина | да | нет |
| Заполнение области | нет | да |
| Подходит для потоков | отлично | средне |
| Подходит для диапазонов | плохо | отлично |
| Свойство | line | trail |
|---|---|---|
| Простота | высокая | средняя |
| Производительность | выше | ниже |
| Визуальная выразительность | ниже | выше |
| Кодирование дополнительной метрики | нет | да |
Оптимальный диапазон:
"range": [2, 15]
Толстые линии требуют хорошей цветовой читаемости.
Подходящие схемы:
"opacity": 0.7
Снижает визуальную перегруженность.
Иногда linear выглядит лучше, чем чрезмерно сглаженные кривые.
vegaEmbed('#view', {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"width": 700,
"height": 300,
"data": {
"values": [
{"time":0,"cpu":20,"requests":100},
{"time":1,"cpu":40,"requests":250},
{"time":2,"cpu":35,"requests":500},
{"time":3,"cpu":60,"requests":700},
{"time":4,"cpu":30,"requests":200}
]
},
"mark": {
"type": "trail",
"interpolate": "monotone",
"opacity": 0.8
},
"encoding": {
"x": {
"field":"time",
"type":"quantitative"
},
"y": {
"field":"cpu",
"type":"quantitative",
"title":"CPU %"
},
"size": {
"field":"requests",
"type":"quantitative",
"scale": {
"range":[2,25]
}
},
"color": {
"field":"cpu",
"type":"quantitative",
"scale":{
"scheme":"plasma"
}
}
}
});
Здесь одновременно отображаются:
Trail позволяет кодировать несколько измерений в одном визуальном объекте без необходимости строить дополнительные графики.