Задержка переходов как функция индекса
Визуальные переходы в D3.js строятся поверх механизма
transition, где каждый элемент набора может иметь
собственные параметры времени. Ключевым инструментом для создания
каскадных (staggered) анимаций выступает функция delay,
принимающая вычисление на основе данных и индекса элемента. Именно
индекс i становится основой синхронизации
последовательностей.
При работе с коллекциями DOM-элементов D3 передаёт в функции доступа два аргумента:
d — связанный с элементом datumi — индекс элемента в текущей выборкеИндекс не зависит от исходного набора данных напрямую, а формируется в момент связывания и обхода selection. Это делает его устойчивым инструментом для построения временных последовательностей.
d3.selectAll("rect")
.transition()
.delay((d, i) => i * 50)
.duration(600)
.attr("y", d => 100 - d);
В данном случае каждый следующий прямоугольник начинает анимацию на 50 мс позже предыдущего, формируя линейный каскад.
Функция вида i * k создаёт равномерную задержку между
элементами:
Такой подход используется в списках, столбчатых диаграммах и любых последовательных структурах.
.delay((d, i) => i * 80)
При большом количестве элементов линейная модель может приводить к заметному растяжению общей длительности анимации, поскольку последний элемент получает максимальную задержку.
Изменение направления каскада достигается инвертированием индекса относительно длины выборки:
const nodes = d3.selectAll("circle");
nodes.transition()
.delay((d, i) => (nodes.size() - i - 1) * 40)
.attr("r", 6);
Такой подход формирует эффект «сборки» с конца массива к началу, часто используемый при обратных анимациях удаления или скрытия элементов.
Линейное распределение не всегда соответствует требуемой динамике. Использование нелинейных функций позволяет управлять плотностью появления элементов.
.delay((d, i) => i * i * 10)
Эффект: первые элементы появляются почти одновременно, дальнейшие — с резко увеличивающимися интервалами.
.delay((d, i) => Math.sqrt(i) * 120)
Эффект: быстрый старт и постепенное сглаживание интервалов.
Более контролируемый способ — использование scaleLinear,
позволяющего нормализовать диапазон индексов в диапазон времени.
const scaleDelay = d3.scaleLinear()
.domain([0, 50])
.range([0, 1000]);
d3.selectAll("rect")
.transition()
.delay((d, i) => scaleDelay(i));
Такой подход особенно полезен при неизвестном заранее размере выборки или при необходимости строгого ограничения общей длительности анимации.
Использование тригонометрических функций создаёт волнообразные эффекты появления:
.delay((d, i) => Math.sin(i * 0.3) * 200 + 300)
В результате элементы не появляются строго последовательно, а формируют визуальные волны. Это применяется в декоративных визуализациях и динамических интерфейсах.
При наличии структурированных данных часто требуется синхронизация по группам. Индекс внутри группы может комбинироваться с внешним индексом.
d3.selectAll(".group")
.selectAll("rect")
.transition()
.delay((d, i, nodes) => i * 30);
При работе с вложенными selection важно учитывать, что i
относится к текущей группе, а не к глобальному набору.
Для глобальной координации используется внешний контекст:
let groupIndex = 0;
d3.selectAll(".group")
.each(function() {
d3.select(this)
.selectAll("rect")
.transition()
.delay((d, i) => i * 20 + groupIndex * 200);
groupIndex++;
});
Staggered-анимации особенно часто применяются в фазе
enter, где новые элементы вводятся в DOM:
const bars = d3.select("svg")
.selectAll("rect")
.data(data);
bars.enter()
.append("rect")
.attr("y", 100)
.attr("height", 0)
.transition()
.delay((d, i) => i * 40)
.attr("height", d => d);
Здесь задержка формирует ощущение постепенного «вырастания» диаграммы.
Эффект каскада определяется не только задержкой, но и соотношением с длительностью анимации:
.transition()
.delay((d, i) => i * 30)
.duration(400)
Если delay существенно меньше duration,
элементы перекрывают друг друга по времени, создавая плотную динамику.
При большом delay возникает строго последовательная
анимация.
Добавление стохастики снижает механистичность анимации:
.delay((d, i) => i * 20 + Math.random() * 100)
Такая схема используется для имитации естественных процессов появления, особенно в плотных визуальных полях.
При тысячах элементов линейное масштабирование задержки становится критичным фактором производительности восприятия. Практика включает:
const maxDelay = 800;
.delay((d, i) => Math.min(i * 10, maxDelay))
Индекс в delay зависит от порядка selection, а не от
визуального положения. Перестановка элементов в DOM меняет поведение
анимации без изменения данных.
d3.selectAll("rect")
.sort((a, b) => a - b)
.transition()
.delay((d, i) => i * 25)
Сортировка позволяет управлять направлением каскада без изменения формулы задержки.
При наличии нескольких свойств, анимируемых одновременно, задержка применяется ко всему transition-объекту:
d3.selectAll("circle")
.transition()
.delay((d, i) => i * 50)
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", 8)
Все атрибуты начинают изменяться одновременно внутри каждого элемента, но сами элементы стартуют по очереди.
При использовании последовательных переходов каждый следующий transition может наследовать временную структуру:
d3.selectAll("rect")
.transition()
.delay((d, i) => i * 30)
.attr("height", d => d)
.transition()
.delay(0)
.attr("opacity", 0.5)
Первый переход формирует каскад, второй запускается после завершения первого для каждого элемента.
Функция delay((d, i) => ...) выступает универсальным
механизмом управления временной логикой визуализаций. Индекс становится
абстрактной осью времени, на которую проецируются различные функции:
Комбинация этих подходов формирует гибкую систему управления последовательностью появления элементов в пространстве SVG и DOM, где временная структура становится частью визуальной композиции.