Генератор площади: d3.area()

Генератор площади d3.area() предназначен для построения площадных графиков, где важна не только линия изменения значений, но и заполненная область под кривой. Он формирует SVG-путь на основе массива данных, интерпретируя каждую точку как координаты верхней и нижней границы области.

Площадной график описывается двумя основными линиями:

  • верхняя граница (обычно значения данных)
  • нижняя граница (часто нулевая линия или другая серия)

Между этими границами формируется залитая область. В d3.area() эта логика инкапсулирована в генератор пути SVG, который автоматически строит замкнутую фигуру.

Генератор возвращает строку для атрибута d элемента <path>.

Базовая структура генератора

Создание генератора площади выполняется через:

const area = d3.area();

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

  • x() — горизонтальная координата
  • y0() — нижняя граница области
  • y1() — верхняя граница области

Простейшая конфигурация:

const area = d3.area()
  .x(d => xScale(d.x))
  .y0(height)
  .y1(d => yScale(d.y));

В этом случае нижняя граница фиксирована на уровне height, что соответствует оси X в нижней части SVG.

Связь с масштабами

Почти всегда d3.area() используется совместно с масштабами d3.scaleLinear() или другими шкалами.

const xScale = d3.scaleLinear()
  .domain([0, 10])
  .range([0, width]);

const yScale = d3.scaleLinear()
  .domain([0, 100])
  .range([height, 0]);

Инверсия диапазона yScale (от height к 0) необходима, так как в SVG ось Y направлена вниз.

После этого генератор связывается со шкалами:

const area = d3.area()
  .x(d => xScale(d.x))
  .y0(height)
  .y1(d => yScale(d.y));

Формирование области между двумя рядами данных

Одно из ключевых применений — визуализация диапазонов или двух временных рядов.

Структура данных:

const data = [
  { x: 0, min: 30, max: 80 },
  { x: 1, min: 20, max: 70 },
  { x: 2, min: 25, max: 90 }
];

Настройка генератора:

const area = d3.area()
  .x(d => xScale(d.x))
  .y0(d => yScale(d.min))
  .y1(d => yScale(d.max));

В этом случае формируется область неопределённости или диапазона значений.

Использование accessors и гибкая настройка данных

Функции доступа могут принимать не только поля объектов, но и индексы:

.x((d, i) => xScale(i))

Это позволяет использовать массивы без структурированных объектов:

const data = [10, 20, 15, 40];

const area = d3.area()
  .x((d, i) => xScale(i))
  .y0(height)
  .y1(d => yScale(d));

Кривые интерполяции

d3.area() поддерживает различные типы сглаживания через метод curve().

Примеры кривых:

  • d3.curveLinear — линейные сегменты
  • d3.curveBasis — сглаженная кривая Безье
  • d3.curveStep — ступенчатая линия
  • d3.curveCardinal — мягкая интерполяция с контролем натяжения

Пример применения:

const area = d3.area()
  .x(d => xScale(d.x))
  .y0(height)
  .y1(d => yScale(d.y))
  .curve(d3.curveBasis);

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

Замкнутые и незамкнутые области

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

  • сначала строится верхняя линия
  • затем нижняя линия в обратном порядке
  • путь закрывается (Z)

Это обеспечивает корректное заполнение через fill.

Работа с null и пропусками данных

При наличии пропущенных значений используется метод:

defined(d => d.value !== null)

Пример:

const area = d3.area()
  .defined(d => d.value != null)
  .x(d => xScale(d.x))
  .y0(height)
  .y1(d => yScale(d.value));

Разрыв данных приводит к разделению области на несколько независимых сегментов.

Построение stacked area chart

Одно из наиболее распространённых применений — стековые диаграммы.

Данные обрабатываются через d3.stack():

const stack = d3.stack()
  .keys(["a", "b", "c"]);

const series = stack(data);

Каждый слой передаётся в d3.area():

const area = d3.area()
  .x(d => xScale(d.data.x))
  .y0(d => yScale(d[0]))
  .y1(d => yScale(d[1]));

Каждый элемент d в stack содержит диапазон [y0, y1], определяющий слой.

Анимация изменения области

SVG-путь, созданный d3.area(), можно анимировать через переходы:

path.datum(data)
  .transition()
  .duration(750)
  .attr("d", area);

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

Внутренний механизм генерации пути

d3.area() строит строку path-данных SVG на основе последовательности команд:

  • M — перемещение в начальную точку
  • L — линия к следующей точке
  • Z — замыкание пути

Пример логики:

  1. проход по точкам верхней границы слева направо
  2. проход по нижней границе справа налево
  3. замыкание контура

Настройка базовых линий

Нижняя линия может быть фиксированной или динамической.

Фиксированная:

.y0(height)

Динамическая:

.y0(d => yScale(d.min))

Также возможно смещение относительно нуля:

.y0(d => yScale(0))
.y1(d => yScale(d.value))

Использование нескольких генераторов

В сложных визуализациях часто комбинируются разные области:

const areaA = d3.area()
  .x(d => xScale(d.x))
  .y0(height)
  .y1(d => yScale(d.a));

const areaB = d3.area()
  .x(d => xScale(d.x))
  .y0(height)
  .y1(d => yScale(d.b));

Это позволяет строить многослойные графики без использования stack.

Оптимизация отрисовки

При больших объёмах данных важны следующие аспекты:

  • предварительное масштабирование данных
  • минимизация вычислений внутри accessors
  • кэширование шкал
  • использование curveLinear для снижения вычислительной нагрузки

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

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

Обычно d3.area() используется вместе с d3.axisBottom() и d3.axisLeft():

svg.append("g")
  .attr("transform", `translate(0, ${height})`)
  .call(d3.axisBottom(xScale));

svg.append("g")
  .call(d3.axisLeft(yScale));

Генератор площади визуально опирается на эти оси, но не зависит от них напрямую.

Геометрическая интерпретация

С точки зрения геометрии, d3.area() формирует многоугольник, аппроксимирующий интеграл функции:

  • верхняя кривая — f(x)
  • нижняя кривая — g(x)

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

Поведение при изменении масштаба

Изменение domain или range шкал приводит к автоматическому пересчёту всех координат:

xScale.domain([0, newMax]);
yScale.range([newHeight, 0]);

После этого достаточно заново применить генератор к данным:

path.attr("d", area);

Особенности SVG-пути

Сформированный путь может быть использован в различных SVG-операциях:

  • заливка (fill)
  • обводка (stroke)
  • клиппинг (clipPath)
  • маскирование (mask)

Площадная форма особенно чувствительна к стилям заливки, так как визуально занимает большую часть графика.

Комбинация с взаимодействием

При добавлении интерактивности генератор остаётся неизменным, а обновляется только входной набор данных:

function update(newData) {
  path.datum(newData).attr("d", area);
}

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