Библиотека d3-annotation предназначена для добавления поясняющих элементов к графикам, построенным на D3.js. Аннотации представляют собой визуальные маркеры, связанные с конкретными точками, областями или объектами на графике, и состоят из текста, линий-соединителей и вспомогательной геометрии.
Ключевая идея заключается в разделении визуализации данных и поясняющего слоя. Аннотации строятся поверх SVG-графики и не вмешиваются в вычисление данных, а лишь интерпретируют уже существующие координаты.
Аннотация в d3-annotation обычно включает:
Библиотека доступна через npm и CDN.
Через npm:
npm install d3-annotation
Подключение в модуле:
import { annotation, annotationLabel, annotationCallout } from "d3-annotation";
Через CDN:
<script src="https://cdn.jsdelivr.net/npm/d3-annotation@2.5.1/dist/d3-annotation.min.js"></script>
После подключения в глобальной области появляется объект
d3.annotation.
Аннотация описывается объектом с координатами и текстом:
const annotations = [
{
note: {
title: "Пиковое значение",
label: "Максимум функции"
},
x: 120,
y: 80,
dx: 30,
dy: -40
}
];
Здесь:
x, y — координаты точки на SVGdx, dy — смещение текста относительно
точкиnote — содержимое аннотацииСоздание аннотационного слоя:
const makeAnnotations = d3.annotation()
.type(d3.annotationLabel)
.annotations(annotations);
d3.select("svg")
.append("g")
.attr("class", "annotations")
.call(makeAnnotations);
Библиотека предоставляет несколько стандартных типов оформления.
Простая подпись без сложного выделения области.
d3.annotationLabel
Используется для минималистичных пояснений, где не требуется рамка или сложная геометрия.
Аннотация с прямоугольной областью и линией-указателем.
d3.annotationCallout
Подходит для выделения точек интереса на диаграммах.
Аннотация с «ломаным» соединителем, создающим эффект углового перехода линии.
d3.annotationCalloutElbow
Часто используется в сложных графиках с плотным размещением элементов.
Выделение круглой области вокруг объекта.
d3.annotationCalloutCircle
Используется для акцентирования точечных значений или событий.
Объект note управляет текстовой частью аннотации.
{
note: {
title: "Заголовок",
label: "Дополнительное описание",
wrap: 150
}
}
Параметр wrap задаёт ширину переноса текста в
пикселях.
Дополнительно можно задавать HTML-стилизацию через классы:
{
note: {
label: "Текст",
className: "custom-note"
}
}
Аннотации часто строятся на основе массива данных.
const data = [
{ x: 10, y: 20, label: "A" },
{ x: 40, y: 80, label: "B" }
];
const annotations = data.map(d => ({
note: { label: d.label },
x: xScale(d.x),
y: yScale(d.y),
dx: 10,
dy: -10
}));
Здесь важно, что координаты преобразуются через шкалы D3
(xScale, yScale), что обеспечивает корректное
позиционирование на графике.
При использовании D3-осей аннотации должны учитывать трансформации координат.
const xScale = d3.scaleLinear()
.domain([0, 100])
.range([0, width]);
const yScale = d3.scaleLinear()
.domain([0, 100])
.range([height, 0]);
Аннотация привязывается уже к вычисленным пикселям:
x: xScale(50),
y: yScale(75)
При изменении масштаба (zoom/pan) аннотации должны обновляться вместе с графиком.
Соединитель управляет линией между текстом и объектом.
{
connector: {
end: "arrow",
type: "line",
lineType: "horizontal"
}
}
Основные параметры:
end — форма конца линии (arrow,
dot)type — тип линииlineType — поведение (прямая, горизонтальная,
вертикальная)Аннотации рендерятся как SVG-элементы и могут стилизоваться через CSS.
.annotation text {
font-size: 12px;
fill: #333;
}
.annotation path {
stroke: #999;
stroke-width: 1px;
}
Можно также переопределять стили отдельных классов:
.annotation.callout {
fill: none;
stroke: black;
}
D3 позволяет анимировать появление аннотаций через стандартные transition.
d3.selectAll(".annotation")
.style("opacity", 0)
.transition()
.duration(800)
.style("opacity", 1);
Также можно анимировать изменение координат:
d3.selectAll(".annotation")
.transition()
.duration(500)
.attr("transform", d => `translate(${d.x}, ${d.y})`);
При изменении данных аннотации пересоздаются или обновляются через
повторный вызов call.
function updateAnnotations(data) {
const annotations = data.map(d => ({
note: { label: d.label },
x: xScale(d.x),
y: yScale(d.y),
dx: 15,
dy: -15
}));
const makeAnnotations = d3.annotation()
.type(d3.annotationCallout)
.annotations(annotations);
svg.select(".annotations")
.call(makeAnnotations);
}
Аннотации можно группировать по слоям для сложных визуализаций:
const layer1 = svg.append("g").attr("class", "annotations-primary");
const layer2 = svg.append("g").attr("class", "annotations-secondary");
Это позволяет разделять смысловые уровни:
Библиотека позволяет создавать собственные типы через расширение.
const customType = d3.annotationCustomType(
d3.annotationCallout,
{
connector: { type: "elbow" }
}
);
const makeAnnotations = d3.annotation()
.type(customType)
.annotations(annotations);
Такой подход используется для стандартизации визуального языка в проекте.
Некорректное позиционирование аннотаций:
Наложение текста:
dx/dyНесогласованность при zoom:
При большом количестве аннотаций используется группировка и фильтрация:
const filtered = annotations.filter(d => d.importance > 5);
Также применяется ленивое отображение:
В комбинированных графиках (линии + бары + scatter) аннотации выступают как независимый слой интерпретации данных. Они не зависят от типа графика, а работают исключительно с координатами SVG.
Типичная структура:
SVG
├── axes
├── chart elements
├── annotations layer
├── overlays
Аннотации располагаются выше всех визуальных элементов для обеспечения читаемости.