Метки на столбцах: позиционирование и форматирование

В столбчатых диаграммах Nivo метки (labels) являются производным элементом, который рассчитывается на основе геометрии каждого бара. Их основная задача — передавать значение серии без необходимости считывания шкалы. В компоненте ResponsiveBar управление отображением меток строится через набор параметров, влияющих на видимость, расположение и условия рендера.

По умолчанию метки размещаются внутри геометрии столбца и центрируются относительно его прямоугольной области. Такое поведение обеспечивает читаемость при достаточной высоте или ширине элемента, но требует настройки при плотной визуализации.

Ключевые аспекты позиционирования:

  • метка привязывается к центру бар-объекта;
  • при горизонтальной ориентации смещается вдоль оси X;
  • при вертикальной ориентации — вдоль оси Y;
  • позиция не задаётся абсолютными координатами, а вычисляется через внутреннюю модель layout.

Контроль смещения возможен через кастомный компонент метки, где координаты x, y, а также размеры width и height передаются как аргументы функции рендера.

Пример кастомного позиционирования:

const CustomLabel = ({ x, y, width, height, value }) => (
    <text
        x={x + width / 2}
        y={y + height / 2}
        textAnchor="middle"
        dominantBaseline="central"
    >
        {value}
    </text>
)

Такой подход позволяет полностью переопределить алгоритм размещения и использовать собственные правила центрирования, выравнивания или смещения.


Управление видимостью меток и условия рендера

Визуально перегруженные диаграммы требуют механизма автоматического скрытия меток. В Nivo он реализован через параметры порогов:

  • labelSkipWidth
  • labelSkipHeight

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

Типичный сценарий:

labelSkipWidth={24}
labelSkipHeight={12}

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

Дополнительно учитывается радиус и геометрия элемента. При сложных наборах данных (например, большое количество категорий) пропуск меток становится основным инструментом оптимизации визуального шума.


Форматирование текста меток

Форматирование значений осуществляется через несколько механизмов, каждый из которых отвечает за определённый уровень преобразования данных.

Функция label

Параметр label определяет, какое значение отображается в метке. По умолчанию используется числовое значение серии, но его можно заменить вычисляемым выражением:

label={d => `${d.value} шт.`}

Функция получает объект данных, содержащий:

  • id серии
  • value
  • index
  • data

Это позволяет строить составные подписи, включающие контекст категории или процентное соотношение.


Форматирование через valueFormat

Альтернативный способ — использование форматтера:

valueFormat={value => `${value.toFixed(1)}%`}

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

Часто применяется при:

  • отображении процентов;
  • финансовых данных;
  • нормализованных метриках.

Цвет меток и контрастность

Контраст текста относительно фона столбца управляется через labelTextColor. Это может быть как фиксированное значение, так и функция.

Функциональный вариант:

labelTextColor={d => (d.value > 50 ? '#ffffff' : '#000000')}

Более универсальный подход — использование контраста относительно заливки:

labelTextColor={{ from: 'color', modifiers: [['darker', 2]] }}

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


Переопределение компонента метки

Полный контроль над отображением достигается через labelComponent. В этом случае Nivo перестаёт рендерить стандартный текст и передаёт управление пользовательскому компоненту.

const Label = ({ x, y, width, height, value, data }) => (
    <g transform={`translate(${x}, ${y})`}>
        <rect width={width} height={height} fill="transparent" />
        <text
            x={width / 2}
            y={height / 2}
            textAnchor="middle"
            dominantBaseline="central"
        >
            {value}
        </text>
    </g>
)

Такой подход применяется при необходимости:

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

Вертикальные и горизонтальные диаграммы

Ориентация графика напрямую влияет на поведение меток.

Вертикальный layout

  • метки располагаются внутри столбцов по вертикали;
  • основное ограничение — высота элемента;
  • чаще используется labelSkipHeight.

Горизонтальный layout

  • метки зависят от ширины столбца;
  • чаще используется labelSkipWidth;
  • повышается читаемость длинных категорий.
layout="horizontal"

В горизонтальном режиме особое значение приобретает выравнивание текста по оси X, особенно при длинных числовых значениях.


Сложные случаи: перекрытие и плотные данные

При большом количестве категорий возникает проблема наложения меток. Стандартные механизмы Nivo решают её комбинацией:

  • автоматического пропуска (skip);
  • уменьшения шрифта через тему;
  • скрытия при недостаточной площади;
  • кастомного рендера.

Дополнительно используется стратегия условного отображения:

label={d => (d.value < 10 ? '' : d.value)}

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


Многоуровневые подписи и контекст

В некоторых сценариях требуется отображение не только значения, но и дополнительного контекста (категория, процент, группа). Это реализуется через составные строки:

label={d => `${d.indexValue}: ${d.value}`}

или через вычисления:

label={d => `${Math.round(d.value / d.total * 100)}%`}

При этом важно учитывать ограничение пространства, особенно в вертикальных диаграммах, где длина текста напрямую влияет на перекрытие.


Адаптация меток под тему и стили

Nivo поддерживает централизованное оформление через theme, включая параметры текста:

  • размер шрифта;
  • семейство шрифтов;
  • межстрочные интервалы;
  • базовый цвет.
theme={{
    labels: {
        text: {
            fontSize: 12,
            fontWeight: 500
        }
    }
}}

Такой подход позволяет синхронизировать метки с общей визуальной системой интерфейса без дублирования настроек в каждом графике.