text

Текстовые элементы играют ключевую роль в визуализации данных: заголовки, подписи осей, аннотации, метки значений, легенды и интерактивные подсказки формируют читаемость графика и влияют на восприятие информации. В экосистеме Vega и Vega-Lite текст представлен как полноценный графический примитив, обладающий собственными свойствами позиционирования, форматирования и поведения.


Текстовые метки в Vega-Lite

В Vega-Lite текст чаще всего используется через mark: "text".

Базовая текстовая визуализация

const spec = {
  data: {
    values: [
      { category: "A", value: 28 },
      { category: "B", value: 55 },
      { category: "C", value: 43 }
    ]
  },

  mark: "text",

  encoding: {
    x: { field: "category", type: "nominal" },
    y: { field: "value", type: "quantitative" },
    text: { field: "value" }
  }
};

В данном примере:

  • ось x определяет горизонтальное положение;
  • ось y — вертикальное;
  • канал text выводит содержимое текстовой метки.

Настройка внешнего вида текста

Размер шрифта

mark: {
  type: "text",
  fontSize: 24
}

Цвет текста

mark: {
  type: "text",
  color: "darkred"
}

Семейство шрифтов

mark: {
  type: "text",
  font: "Arial"
}

Толщина шрифта

mark: {
  type: "text",
  fontWeight: "bold"
}

Допустимые значения:

  • "normal"
  • "bold"
  • числовые значения: 100, 200, 400, 700

Выравнивание текста

Текстовые элементы можно выравнивать относительно координаты.

Горизонтальное выравнивание

mark: {
  type: "text",
  align: "center"
}

Варианты:

  • "left"
  • "center"
  • "right"

Вертикальное выравнивание

mark: {
  type: "text",
  baseline: "middle"
}

Варианты:

  • "top"
  • "middle"
  • "bottom"
  • "alphabetic"

Смещение текста

Часто текст необходимо немного сдвинуть относительно точки.

Смещение по оси X

mark: {
  type: "text",
  dx: 10
}

Смещение по оси Y

mark: {
  type: "text",
  dy: -10
}

Поворот текста

mark: {
  type: "text",
  angle: -45
}

Полезно для:

  • длинных подписей;
  • диаграмм с плотным расположением элементов;
  • визуализации категорий.

Текст поверх столбцов

Одна из наиболее распространённых задач — отображение числовых значений поверх баров.

const spec = {
  data: {
    values: [
      { month: "Jan", sales: 120 },
      { month: "Feb", sales: 98 },
      { month: "Mar", sales: 143 }
    ]
  },

  layer: [
    {
      mark: "bar",

      encoding: {
        x: { field: "month", type: "ordinal" },
        y: { field: "sales", type: "quantitative" }
      }
    },

    {
      mark: {
        type: "text",
        dy: -10,
        color: "black"
      },

      encoding: {
        x: { field: "month", type: "ordinal" },
        y: { field: "sales", type: "quantitative" },
        text: { field: "sales" }
      }
    }
  ]
};

Используется механизм layer, позволяющий накладывать несколько визуальных слоёв.


Форматирование чисел

Текстовые значения могут автоматически форматироваться.

Формат через format

text: {
  field: "price",
  format: "$.2f"
}

Результат:

$12.45

Формат процентов

text: {
  field: "ratio",
  format: ".1%"
}

Результат:

45.3%

Форматирование дат

text: {
  field: "date",
  type: "temporal",
  format: "%d.%m.%Y"
}

Популярные спецификаторы:

Формат Описание
%Y год
%m месяц
%d день
%H часы
%M минуты

Условный текст

Vega-Lite поддерживает условные выражения.

Изменение цвета текста

color: {
  condition: {
    test: "datum.value > 50",
    value: "green"
  },
  value: "red"
}

Изменение содержимого

text: {
  condition: {
    test: "datum.value > 100",
    value: "High"
  },
  value: "Low"
}

Вычисляемый текст

Через calculate можно создавать динамические подписи.

transform: [
  {
    calculate: "datum.sales + ' units'",
    as: "label"
  }
]

Далее:

text: {
  field: "label"
}

Многострочный текст

Использование символа перевода строки

calculate: "'Line 1\\nLine 2'"

Ограничение длины текста

Свойство limit

mark: {
  type: "text",
  limit: 100
}

Текст автоматически обрезается при превышении ширины.


Эллипсис

mark: {
  type: "text",
  ellipsis: "..."
}

Интерактивный текст

Текст может реагировать на наведение и события.

Изменение размера при наведении

encoding: {
  size: {
    condition: {
      param: "hover",
      value: 20
    },
    value: 12
  }
}

Описание параметра

params: [
  {
    name: "hover",
    select: {
      type: "point",
      on: "mouseover"
    }
  }
]

Tooltip и текст

Текстовые элементы часто комбинируются с всплывающими подсказками.

encoding: {
  tooltip: [
    { field: "name" },
    { field: "value" }
  ]
}

Текст в Vega

В Vega текст задаётся через объект marks.

Базовый пример

{
  "type": "text",

  "encode": {
    "enter": {
      "x": { "value": 100 },
      "y": { "value": 50 },

      "text": {
        "value": "Hello Vega"
      },

      "fontSize": {
        "value": 24
      }
    }
  }
}

Секции encode

В Vega состояние элемента разделяется на несколько фаз.

Секция Назначение
enter начальное состояние
update обновление
hover состояние наведения
exit удаление

Динамический текст в Vega

"text": {
  "signal": "datum.value"
}

Сигналы (signals) позволяют динамически вычислять значения.


Использование сигналов

Определение сигнала

signals: [
  {
    name: "fontSize",
    value: 18
  }
]

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

fontSize: {
  signal: "fontSize"
}

Анимация текста

Vega поддерживает реактивное обновление текста через сигналы.

signals: [
  {
    name: "label",
    value: "Start",

    on: [
      {
        events: "click",
        update: "'Clicked'"
      }
    ]
  }
]

Привязка текста к данным

from: {
  data: "table"
}
text: {
  field: "name"
}

Автоматическая генерация подписей

{
  type: "text",

  from: {
    data: "table"
  },

  encode: {
    enter: {
      x: { field: "x" },
      y: { field: "y" },

      text: {
        field: "label"
      }
    }
  }
}

Text mark как аннотация

Текстовые метки активно используются для пояснений на графике.

{
  type: "text",

  encode: {
    enter: {
      x: { value: 300 },
      y: { value: 100 },

      text: {
        value: "Peak value"
      },

      fill: {
        value: "red"
      }
    }
  }
}

Комбинирование text и rule

Аннотации часто состоят из линии и подписи.

layer: [
  {
    mark: "rule"
  },

  {
    mark: "text"
  }
]

Настройка прозрачности

mark: {
  type: "text",
  opacity: 0.5
}

Cursor для текста

mark: {
  type: "text",
  cursor: "pointer"
}

Ограничение области отображения

mark: {
  type: "text",
  clip: true
}

RTL и Unicode

Vega корректно работает с Unicode-символами.

text: {
  value: "Пример текста"
}

Поддерживаются:

  • кириллица;
  • арабский;
  • китайский;
  • emoji;
  • комбинированные символы Unicode.

Производительность при большом количестве текста

Текстовые элементы являются дорогими для рендеринга, особенно в SVG.

Основные рекомендации

  • использовать Canvas renderer;
  • избегать тысяч текстовых меток;
  • скрывать второстепенные подписи;
  • применять агрегацию;
  • использовать limit.

Canvas и SVG

SVG

Преимущества:

  • высокое качество;
  • удобство стилизации;
  • DOM-доступ.

Недостатки:

  • медленная работа при большом количестве элементов.

Canvas

Преимущества:

  • высокая производительность;
  • эффективный рендеринг.

Недостатки:

  • отсутствие DOM-узлов;
  • сложнее интерактивность.

Настройка renderer

vegaEmbed("#view", spec, {
  renderer: "canvas"
});

Текстовые оси

Подписи осей автоматически генерируются системой шкал.

Поворот подписей

axis: {
  labelAngle: -45
}

Размер шрифта оси

axis: {
  labelFontSize: 14
}

Заголовки графиков

title: {
  text: "Sales by Month",
  fontSize: 24,
  color: "darkblue"
}

Подзаголовки

title: {
  text: "Main Title",
  subtitle: "Subtitle"
}

Настройка легенд

legend: {
  labelFontSize: 12,
  titleFontSize: 14
}

Формирование сложных подписей

Конкатенация

calculate: "datum.name + ': ' + datum.value"

Форматирование чисел

calculate: "format(datum.value, '.2f')"

Выражения Vega

В текстовых вычислениях часто используются expression functions.

Пример

calculate: "upper(datum.category)"

Полезные функции

Функция Назначение
upper() верхний регистр
lower() нижний регистр
substring() подстрока
length() длина строки
format() форматирование

Адаптивный текст

Динамический размер

fontSize: {
  signal: "width / 30"
}

Скрытие текста

opacity: {
  signal: "datum.value > 10 ? 1 : 0"
}

Heatmap с текстом

layer: [
  {
    mark: "rect"
  },

  {
    mark: {
      type: "text",
      color: "white"
    }
  }
]

Scatter Plot с подписями

mark: {
  type: "text",
  dx: 8
}

Bubble Chart с текстом

Размер текста может зависеть от данных.

encoding: {
  size: {
    field: "population"
  }
}

Label overlap

Проблема наложения текста является одной из главных в визуализации.

Способы решения

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

Использование transform для подписей

transform: [
  {
    window: [
      {
        op: "rank",
        as: "rank"
      }
    ]
  }
]

Можно выводить только топ-N элементов.


Text mark и accessibility

Текст улучшает доступность визуализации:

  • помогает screen readers;
  • повышает читаемость;
  • облегчает интерпретацию данных.

Практический пример: подписи в pie chart

const spec = {
  data: {
    values: [
      { category: "A", value: 40 },
      { category: "B", value: 25 },
      { category: "C", value: 35 }
    ]
  },

  layer: [
    {
      mark: {
        type: "arc",
        outerRadius: 120
      },

      encoding: {
        theta: {
          field: "value",
          type: "quantitative"
        },

        color: {
          field: "category",
          type: "nominal"
        }
      }
    },

    {
      mark: {
        type: "text",
        radius: 140
      },

      encoding: {
        theta: {
          field: "value",
          type: "quantitative"
        },

        text: {
          field: "category"
        }
      }
    }
  ]
};

Практический пример: интерактивные подписи

const spec = {
  params: [
    {
      name: "hover",
      select: {
        type: "point",
        on: "mouseover"
      }
    }
  ],

  mark: "text",

  encoding: {
    x: { field: "x", type: "quantitative" },
    y: { field: "y", type: "quantitative" },

    text: { field: "label" },

    color: {
      condition: {
        param: "hover",
        value: "red"
      },

      value: "black"
    }
  }
};