Линейный генератор в D3.js представляет собой инструмент
преобразования набора координатных данных в строку пути SVG. Основная
задача d3.line() заключается в создании значения атрибута
d для элемента <path>, на основе массива
точек. Каждая точка интерпретируется как пара координат, а результатом
становится последовательность команд SVG Path Data.
Функция не выполняет визуализацию самостоятельно, а формирует строковое представление геометрии, которое затем интерпретируется браузером.
Базовая структура:
const line = d3.line();
При вызове генератора с массивом данных:
const data = [
[0, 10],
[100, 50],
[200, 30]
];
const pathData = line(data);
результатом будет строка вида:
M0,10L100,50L200,30
SVG интерпретирует M как перемещение пера в начальную
точку и L как проведение линии к следующей точке.
По умолчанию d3.line() ожидает массив массивов из двух
элементов:
[x, y]
Однако структура данных может быть произвольной. Для этого используются аксессоры.
const data = [
{ time: 0, value: 10 },
{ time: 1, value: 40 },
{ time: 2, value: 25 }
];
Определение доступа к координатам:
const line = d3.line()
.x(d => d.time * 100)
.y(d => d.value);
Ключевой механизм работы линейного генератора — функции-аксессоры.
.x(d => d[0])
или
.x(d => xScale(d.time))
Определяет горизонтальную координату точки.
.y(d => d[1])
или
.y(d => yScale(d.value))
Определяет вертикальную координату.
Важно учитывать, что в SVG ось Y направлена вниз, поэтому часто применяется инвертированное масштабирование:
.y(d => height - yScale(d.value))
Линейный генератор часто используется вместе со шкалами D3:
const xScale = d3.scaleLinear()
.domain([0, 10])
.range([0, 500]);
const yScale = d3.scaleLinear()
.domain([0, 100])
.range([300, 0]);
const line = d3.line()
.x(d => xScale(d.x))
.y(d => yScale(d.y));
Шкалы обеспечивают преобразование данных в координаты SVG, сохраняя пропорции и масштаб графика.
Результат работы d3.line() — строка SVG Path Data.
Основные команды:
M x,y — перемещение в первую точкуL x,y — линия к следующей точкеПример:
const data = [
{ x: 0, y: 0 },
{ x: 50, y: 80 },
{ x: 100, y: 20 }
];
const line = d3.line()
.x(d => d.x)
.y(d => d.y);
console.log(line(data));
Результат:
M0,0L50,80L100,20
d3.line() поддерживает разрывы линии при наличии
отсутствующих данных.
const data = [
{ x: 0, y: 10 },
{ x: 1, y: 20 },
{ x: 2, y: null },
{ x: 3, y: 40 }
];
По умолчанию null может нарушить построение. Для
управления используется defined():
const line = d3.line()
.defined(d => d.y != null)
.x(d => xScale(d.x))
.y(d => yScale(d.y));
При встрече с false генератор разрывает путь и начинает
новый сегмент.
Результат будет содержать несколько независимых линий.
Хотя d3.line() по умолчанию создаёт ломаную линию, форма
может быть изменена через интерполяцию (curve).
const line = d3.line()
.curve(d3.curveLinear);
Основные варианты кривых:
d3.curveLinear — прямая ломанаяd3.curveBasis — сглаженная криваяd3.curveCardinal — контролируемая гладкостьd3.curveStep — ступенчатая линияПример сглаживания:
const line = d3.line()
.x(d => xScale(d.x))
.y(d => yScale(d.y))
.curve(d3.curveBasis);
Это преобразует резкие углы в плавную кривую Безье.
Линейный генератор может замыкать контур, превращая линию в замкнутую фигуру.
const line = d3.line()
.x(d => xScale(d.x))
.y(d => yScale(d.y))
.closed(true);
Замкнутые линии полезны при построении многоугольников и радарных диаграмм.
В реальных визуализациях данные редко совпадают с координатами SVG напрямую. Часто используется система отступов (margin convention).
const width = 600;
const height = 400;
const svg = d3.select("svg")
.attr("width", width)
.attr("height", height);
const g = svg.append("g")
.attr("transform", "translate(40,20)");
Линейный генератор остаётся независимым от DOM:
const line = d3.line()
.x(d => xScale(d.x))
.y(d => yScale(d.y));
Трансформация применяется на уровне контейнера
<g>.
Создание линии в DOM:
svg.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 2)
.attr("d", line);
Метод datum() связывает данные с элементом, а
line автоматически преобразует их в путь.
При изменении данных генератор пересчитывает путь:
function update(data) {
svg.select("path")
.datum(data)
.attr("d", line);
}
Таким образом, визуализация отделена от логики данных.
Линейный генератор часто применяется для временных данных:
const data = [
{ date: new Date(2020, 0, 1), value: 30 },
{ date: new Date(2020, 1, 1), value: 50 },
{ date: new Date(2020, 2, 1), value: 40 }
];
const xScale = d3.scaleTime()
.domain(d3.extent(data, d => d.date))
.range([0, width]);
const line = d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.value));
Здесь используется временная шкала, обеспечивающая корректное распределение точек по оси X.
При работе с несколькими наборами данных создаются независимые генераторы или используется один универсальный:
const line = d3.line()
.x(d => xScale(d.x))
.y(d => yScale(d.y));
svg.selectAll(".series")
.data(series)
.enter()
.append("path")
.attr("class", "series")
.attr("d", d => line(d.values));
Каждая серия данных преобразуется в отдельный SVG path.
d3.line() оптимизирован для быстрого преобразования
массивов в строку. Однако при больших наборах данных важны следующие
аспекты:
Линейный генератор часто комбинируется с:
d3.axisBottom, d3.axisLeft для осейd3.scaleLinear, d3.scaleTime для шкалd3.brush для интерактивного выделенияd3.zoom для масштабирования графикаТакая связка формирует основу большинства линейных графиков в D3.js.
Частые проблемы:
fill="none" для path, из-за чего линия
перекрывает графикКорректная настройка генератора и данных критична для стабильного результата.