Аннотации ячеек в визуализациях Nivo представляют собой механизм обогащения отдельных элементов графика дополнительной информацией, которая не входит в основную шкалу данных, но существенно расширяет интерпретацию значений. Чаще всего этот подход применяется в компонентах, где данные представлены в виде дискретных ячеек: heatmap, calendar, treemap и некоторых кастомных визуализациях.
Основная задача аннотаций заключается в связывании конкретной ячейки с дополнительным текстовым или графическим содержимым, которое отображается поверх или рядом с элементом графика. Это может быть числовая метка, процентное значение, категория, статус или даже условная подсказка, формируемая на основе бизнес-логики.
В основе работы аннотаций лежит принцип расширения рендеринга отдельного элемента через пользовательские функции. В Nivo каждая ячейка графика является результатом вычисления набора данных и может быть модифицирована на этапе отрисовки.
Ключевая идея:
В большинстве компонентов Nivo это реализуется через функции
tooltip, label или custom layer,
однако для ячеек чаще используется кастомный рендеринг внутри
cell или renderCell.
Heatmap является основным компонентом, где концепция аннотированных ячеек используется наиболее явно. Каждая ячейка представляет пересечение двух осей и содержит числовое значение.
Типовая структура данных:
const data = [
{
id: 'group A',
data: [
{ x: 'item 1', y: 12 },
{ x: 'item 2', y: 34 }
]
}
];
Аннотация ячейки формируется через cellComponent или
tooltip, а также через кастомные слои.
Пример расширенного рендеринга:
<HeatMap
data={data}
keys={['value']}
indexBy="id"
cellComponent={({ x, y, value }) => {
return (
<g>
<rect x={x} y={y} width={20} height={20} fill="#ddd" />
<text x={x + 10} y={y + 12} textAnchor="middle">
{value}
</text>
</g>
);
}}
/>
Здесь текст внутри text является аннотацией ячейки,
отображающей значение прямо поверх графического элемента.
Аннотации часто зависят от значения ячейки. Это позволяет визуально выделять критические зоны данных.
Пример условной логики:
cellComponent={({ x, y, value }) => {
const isHigh = value > 50;
return (
<g>
<rect
x={x}
y={y}
width={20}
height={20}
fill={isHigh ? '#e74c3c' : '#2ecc71'}
/>
{isHigh && (
<text x={x + 10} y={y + 12} textAnchor="middle" fill="#fff">
!
</text>
)}
</g>
);
}}
В данном случае аннотация в виде символа появляется только при превышении порогового значения. Такой подход применяется для визуализации аномалий, пиков или ошибок.
Некоторые компоненты Nivo поддерживают систему слоёв
(layers), позволяющую добавлять аннотации поверх всей сетки
ячеек.
Структура слоя:
Пример кастомного слоя:
const CustomAnnotations = ({ cells }) => {
return (
<g>
{cells.map(cell => {
if (cell.value > 80) {
return (
<text
x={cell.x + cell.size / 2}
y={cell.y + cell.size / 2}
textAnchor="middle"
fill="black"
>
peak
</text>
);
}
return null;
})}
</g>
);
};
Добавление слоя:
<HeatMap
data={data}
layers={['cells', CustomAnnotations]}
/>
Такой подход отделяет визуализацию данных от логики аннотирования, что особенно важно при масштабировании интерфейсов.
В календарных тепловых картах каждая ячейка соответствует дате. Аннотации здесь часто используются для отображения событий, интенсивности или статуса дня.
Пример:
<Calendar
data={data}
renderCell={({ date, value, x, y, size }) => (
<g>
<rect x={x} y={y} width={size} height={size} fill="#ccc" />
<text x={x + size / 2} y={y + size / 2}>
{value}
</text>
</g>
)}
/>
Аннотация может включать:
В сложных визуализациях одна ячейка может содержать несколько уровней аннотаций:
Пример композиции:
<g>
<rect />
<text>{value}</text>
<circle r={3} fill={statusColor} />
{trend === 'up' && <path d="..." />}
</g>
Такой подход используется в аналитических дашбордах, где одна ячейка должна передавать несколько смысловых слоёв информации одновременно.
При увеличении количества ячеек аннотации становятся фактором нагрузки на рендеринг. Основные оптимизации включают:
React.memo для cell-компонентовОсобенно критично это для heatmap с тысячами элементов, где каждая аннотация добавляет дополнительный DOM-узел.
Аннотации могут изменяться в зависимости от состояния приложения. При изменении данных Nivo пересчитывает только затронутые ячейки, однако при сложной логике аннотаций важно контролировать стабильность ссылок.
Пример динамического переключения режима аннотаций:
const getAnnotation = (value, mode) => {
if (mode === 'percent') return `${value}%`;
if (mode === 'absolute') return value;
return null;
};
Это позволяет переключать представление без изменения структуры данных.
Аннотации могут быть связаны с пользовательским взаимодействием. Часто используется:
Пример hover-логики:
onMouseEn ter={(cell) => setActive(cell.id)}
И отображение дополнительной информации только для активной ячейки:
{activeId === id && (
<text>{extraInfo}</text>
)}
Такой подход снижает визуальный шум и концентрирует внимание на выбранных данных.
Позиционирование аннотаций внутри ячеек требует учёта:
Часто используется центрирование:
x = cell.x + cell.width / 2
y = cell.y + cell.height / 2
При этом важно учитывать трансформации SVG-контекста, особенно при zoom или responsive layout.
Аннотации редко используются изолированно. Обычно они дополняют цветовую шкалу:
Это снижает неоднозначность интерпретации heatmap и улучшает читаемость плотных данных.