Система аннотаций в Nivo

Аннотации в Nivo представляют собой слой смысловых отметок, накладываемых поверх графиков для выделения ключевых точек, диапазонов или событий. Они не изменяют данные, но добавляют контекст, который помогает интерпретировать визуализацию на уровне бизнес-логики или аналитических выводов.

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

Архитектура аннотаций в Nivo

Система аннотаций интегрирована в рендеринг каждого графика и работает поверх основной сцены визуализации. В зависимости от типа графика (линейный, точечный, столбчатый) используется SVG- или Canvas-реализация, но логика описания аннотаций остаётся единообразной.

Аннотации в Nivo строятся вокруг следующих принципов:

  • привязка к данным через datum, x/y координаты или диапазоны;
  • независимость от серии данных;
  • вычисление позиции через шкалы (scales), используемые графиком;
  • поддержка анимаций совместно с основными элементами;
  • возможность кастомного рендера.

Каждая аннотация рассматривается как отдельный визуальный примитив, накладываемый после отрисовки серии данных.

Базовая структура аннотаций

В большинстве компонентов Nivo (например, ResponsiveLine, ResponsiveBar) аннотации задаются через свойство annotations. Это массив объектов, описывающих поведение и внешний вид отметок.

Типовая структура:

const annotations = [
  {
    type: 'line',
    match: {
      axis: 'x',
      value: 50
    },
    note: 'Пороговое значение',
    notePosition: 'top',
    offset: 6
  }
];

Каждая аннотация содержит несколько ключевых элементов:

  • type — определяет форму (линия, прямоугольник, окружность и т.д.);
  • match — правило привязки к данным или осям;
  • note — текстовое описание;
  • notePosition — расположение подписи относительно объекта;
  • offset — смещение подписи.

Типы аннотаций

Линейные аннотации

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

{
  type: 'line',
  match: { axis: 'y', value: 100 },
  note: 'Целевой уровень',
  notePosition: 'right'
}

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

Прямоугольные аннотации

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

{
  type: 'rect',
  match: {
    axis: 'x',
    start: 20,
    end: 40
  },
  note: 'Область стабильности',
  notePosition: 'top-left'
}

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

Круговые аннотации

Круговые аннотации применяются в точечных графиках и scatter-представлениях. Они позволяют акцентировать внимание на конкретной точке данных.

{
  type: 'circle',
  match: {
    serieId: 'sales',
    x: 30,
    y: 80
  },
  note: 'Аномальное значение',
  notePosition: 'bottom'
}

При использовании с анимацией круг «прикрепляется» к точке даже при изменении масштаба или фильтрации данных.

Границы (bounds)

Аннотации типа bounds создают выделенные области вокруг набора данных или диапазона осей. Они работают как вычисляемые контейнеры.

{
  type: 'bounds',
  match: {
    axis: 'x',
    start: 10,
    end: 60
  },
  note: 'Период кампании'
}

В отличие от rect, границы часто рассчитываются на основе агрегированных данных.

Механизм сопоставления match

Ключевой частью системы является объект match, определяющий, к каким данным применяется аннотация. Поддерживаются несколько стратегий:

  • привязка к оси (axis);
  • привязка к конкретному значению (value);
  • привязка к серии (serieId);
  • привязка к точке данных (x, y);
  • диапазонное сопоставление (start, end).

Комбинирование этих параметров позволяет создавать сложные правила выделения.

{
  type: 'line',
  match: {
    axis: 'y',
    value: 75,
    serieId: 'revenue'
  }
}

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

Подписи и их позиционирование

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

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

  • top
  • bottom
  • left
  • right
  • top-left
  • top-right
  • bottom-left
  • bottom-right
{
  type: 'line',
  match: { axis: 'x', value: 25 },
  note: 'Средняя точка',
  notePosition: 'bottom-right',
  offset: 10
}

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

Кастомизация внешнего вида

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

{
  type: 'rect',
  match: { axis: 'x', start: 10, end: 20 },
  style: {
    fill: 'rgba(255, 0, 0, 0.1)',
    stroke: '#ff0000',
    strokeWidth: 2
  }
}

Дополнительно доступна кастомизация текста:

{
  type: 'line',
  match: { axis: 'y', value: 50 },
  note: 'Порог',
  noteTextStyle: {
    fontSize: 12,
    fontWeight: 600,
    fill: '#333'
  }
}

Анимация аннотаций

Аннотации участвуют в общей системе анимаций Nivo. При изменении данных или масштабов они плавно пересчитывают своё положение.

Анимация включает:

  • переход позиции при изменении шкал;
  • плавное появление и исчезновение;
  • синхронизацию с анимацией серии данных.

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

{
  type: 'circle',
  match: { x: 40, y: 60 },
  animate: true,
  transitionDuration: 800
}

Порядок рендеринга и слои

Аннотации рендерятся поверх основных графических элементов, но внутри общего SVG/Canvas контекста графика. Порядок отрисовки определяется внутренними слоями:

  1. оси и сетка;
  2. серии данных;
  3. области заливки;
  4. аннотации;
  5. подписи и интерактивные элементы.

Такой порядок гарантирует, что аннотации всегда остаются видимыми и не перекрываются данными.

Использование пользовательских аннотаций

Система допускает создание кастомных аннотаций через функцию рендера. Это позволяет интегрировать сложные визуальные элементы: иконки, мини-графики, маркеры состояния.

{
  type: 'custom',
  match: { x: 30, y: 70 },
  render: ({ x, y }) => (
    <g transform={`translate(${x}, ${y})`}>
      <circle r={6} fill="red" />
      <text x={10} y={4}>Critical</text>
    </g>
  )
}

Кастомные аннотации получают доступ к вычисленным координатам, что позволяет интегрировать любые SVG-композиции.

Интеграция с временными рядами

В линейных графиках аннотации особенно эффективны при работе с временными данными. Они позволяют выделять события на временной шкале без изменения структуры данных.

{
  type: 'line',
  match: {
    axis: 'x',
    value: '2024-01-01'
  },
  note: 'Запуск кампании'
}

При масштабировании временной оси аннотация сохраняет привязку к дате, а не к визуальной позиции.

Ограничения и особенности поведения

Система аннотаций имеет ряд особенностей:

  • сложные вычисления диапазонов могут влиять на производительность при большом количестве аннотаций;
  • пересечение аннотаций не обрабатывается автоматически;
  • при Canvas-рендеринге текстовые элементы имеют ограничения по стилизации;
  • динамическое обновление аннотаций требует повторного пересчёта шкал.

Эти особенности определяют необходимость оптимизации при работе с большими наборами визуальных подсказок.

Связь аннотаций с темизацией

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

theme: {
  annotations: {
    text: {
      fontSize: 11,
      fill: '#666'
    },
    line: {
      strokeWidth: 1
    }
  }
}

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