Длительность и задержка: .duration() и .delay()

Анимации в D3.js строятся на механизме переходов — transitions. Именно переходы позволяют плавно изменять атрибуты, стили, размеры, координаты и другие свойства SVG-элементов.

Два ключевых метода управления временем анимации:

  • .duration() — задаёт продолжительность анимации;
  • .delay() — задаёт задержку перед её запуском.

Эти методы используются внутри объекта transition и определяют временное поведение визуализации.


Метод .duration()

Назначение

Метод .duration() задаёт длительность выполнения анимации в миллисекундах.

selection
    .transition()
    .duration(1000)

В данном примере анимация будет длиться одну секунду.


Базовый пример анимации

<svg width="500" height="120"></svg>

<script>
const svg = d3.select("svg");

svg.append("circle")
    .attr("cx", 50)
    .attr("cy", 60)
    .attr("r", 25)
    .attr("fill", "steelblue")
    .transition()
    .duration(2000)
    .attr("cx", 450);
</script>

Что происходит:

  1. Создаётся круг.
  2. Начальная координата cx = 50.
  3. Запускается transition.
  4. В течение 2 секунд круг перемещается вправо.

Как работает длительность

D3.js вычисляет промежуточные значения между начальным и конечным состоянием.

Например:

.attr("cx", 450)

При duration 2000 библиотека:

  • определяет стартовое значение;
  • определяет конечное значение;
  • разбивает изменение на множество кадров;
  • обновляет атрибут примерно 60 раз в секунду.

Нулевая длительность

.duration(0)

Изменение произойдёт практически мгновенно.

Однако transition всё равно создаётся, поэтому:

  • события transition продолжают работать;
  • цепочки анимаций сохраняются;
  • задержки могут применяться.

Изменение нескольких свойств

rect.transition()
    .duration(1500)
    .attr("width", 300)
    .attr("height", 120)
    .style("fill", "orange");

Все свойства будут изменяться одновременно.


Разная длительность для разных элементов

.duration() может принимать функцию.

selection.transition()
    .duration((d, i) => i * 500);

Аргументы:

Аргумент Значение
d данные элемента
i индекс элемента

Пример каскадной скорости

svg.selectAll("circle")
    .transition()
    .duration((d, i) => 1000 + i * 300)
    .attr("cy", 150);

Каждый следующий круг будет двигаться дольше предыдущего.


Метод .delay()

Назначение

.delay() задаёт паузу перед стартом анимации.

.transition()
.delay(1000)

Анимация начнётся через одну секунду.


Пример задержки

rect.transition()
    .delay(2000)
    .duration(1000)
    .attr("x", 300);

Поведение:

  • 2 секунды ожидания;
  • 1 секунда движения.

Разница между .delay() и .duration()

Метод Назначение
.delay() ожидание перед запуском
.duration() время выполнения анимации

Временная схема

.transition()
.delay(1000)
.duration(2000)

Структура времени:

0ms ----- 1000ms ----- 3000ms
         старт       завершение

Последовательные анимации

D3.js позволяет выстраивать transition-цепочки.

circle.transition()
    .duration(1000)
    .attr("cx", 200)
    .transition()
    .duration(1000)
    .attr("cy", 150);

Сначала выполнится первое движение, затем второе.


Использование .delay() для каскадных эффектов

Один из самых популярных приёмов в D3.js — последовательное появление элементов.

svg.selectAll("rect")
    .transition()
    .delay((d, i) => i * 200)
    .duration(800)
    .attr("height", d => d);

Каждый следующий прямоугольник начнёт анимацию позже предыдущего.


Анимация столбчатой диаграммы

<svg width="600" height="300"></svg>

<script>
const data = [40, 80, 120, 160, 100];

const svg = d3.select("svg");

svg.selectAll("rect")
    .data(data)
    .enter()
    .append("rect")
    .attr("x", (d, i) => i * 100 + 30)
    .attr("y", 250)
    .attr("width", 60)
    .attr("height", 0)
    .attr("fill", "teal")
    .transition()
    .delay((d, i) => i * 300)
    .duration(1200)
    .attr("y", d => 250 - d)
    .attr("height", d => d);
</script>

Особенности:

  • столбцы появляются по очереди;
  • используется индивидуальная задержка;
  • создаётся эффект «роста» графика.

Функции внутри .delay()

.delay() также поддерживает функцию.

.delay((d, i) => i * 100)

Это особенно полезно при:

  • анимации списков;
  • диаграмм;
  • сеток;
  • последовательных визуальных эффектах.

Комбинирование задержки и длительности

.transition()
.delay(500)
.duration(1500)

Часто используется структура:

.delay((d, i) => i * 100)
.duration(1000)

Так создаётся:

  • последовательный старт;
  • одинаковая скорость анимации.

Синхронные и асинхронные переходы

Синхронная анимация

selection.transition()
    .duration(1000)
    .attr("opacity", 1);

Все элементы начинают одновременно.


Асинхронная анимация

selection.transition()
    .delay((d, i) => i * 150)
    .duration(1000)
    .attr("opacity", 1);

Элементы запускаются по очереди.


Использование easing-функций

.duration() особенно важен при использовании easing.

.transition()
.duration(2000)
.ease(d3.easeBounce)

Чем длиннее duration:

  • тем заметнее easing;
  • тем плавнее движение;
  • тем выразительнее анимация.

Пример с easing

circle.transition()
    .duration(3000)
    .ease(d3.easeElastic)
    .attr("cx", 400);

Круг будет двигаться с эффектом упругости.


Анимация появления

text
    .style("opacity", 0)
    .transition()
    .duration(1000)
    .style("opacity", 1);

Элемент плавно появляется.


Анимация исчезновения

text.transition()
    .duration(800)
    .style("opacity", 0);

Пошаговая анимация

circle.transition()
    .duration(1000)
    .attr("cx", 200)
    .transition()
    .duration(1000)
    .attr("cy", 200)
    .transition()
    .duration(1000)
    .attr("r", 50);

Получается трёхэтапная анимация:

  1. движение по X;
  2. движение по Y;
  3. изменение радиуса.

Параллельные transition

Если transitions запускаются отдельно:

circle.transition()
    .duration(1000)
    .attr("cx", 300);

circle.transition()
    .duration(1000)
    .attr("cy", 200);

Второй transition может прервать первый.


Почему transition прерываются

В D3.js один элемент может иметь ограниченное количество активных переходов с одинаковым именем.

Новый transition:

  • отменяет предыдущий;
  • перезаписывает таймеры;
  • заменяет анимацию.

Именованные transition

const t = d3.transition()
    .duration(1000);

circle.transition(t)
    .attr("cx", 200);

rect.transition(t)
    .attr("x", 300);

Оба элемента используют один transition.


Динамическое управление временем

Время можно вычислять программно.

.duration(d => d * 100)

Например:

  • большие значения анимируются дольше;
  • маленькие — быстрее.

Адаптивная скорость

.transition()
.duration(d => Math.abs(d) * 20)

Чем больше изменение, тем длиннее анимация.


Использование случайных задержек

.delay(() => Math.random() * 2000)

Подходит для:

  • генеративной графики;
  • эффектов частиц;
  • случайных появлений.

Производительность

Слишком длинные анимации:

  • ухудшают восприятие интерфейса;
  • делают визуализацию медленной;
  • создают ощущение «торможения».

Слишком короткие:

  • выглядят резко;
  • теряют плавность;
  • становятся незаметными.

Практические рекомендации

Интерфейсные анимации

150–400 ms

Графики и диаграммы

600–1500 ms

Демонстрационные эффекты

2000+ ms

Типичные ошибки

Слишком большая задержка

.delay(10000)

Пользователь может решить, что интерфейс завис.


Огромная длительность

.duration(15000)

Анимация становится утомительной.


Отсутствие задержек в сложных сценах

Когда десятки элементов запускаются одновременно:

  • визуализация выглядит хаотично;
  • сложно проследить изменения.

Комбинация enter + delay

Очень распространённый паттерн:

selection.enter()
    .append("circle")
    .attr("r", 0)
    .transition()
    .delay((d, i) => i * 50)
    .duration(500)
    .attr("r", 20);

Элементы появляются постепенно.


Работа с обновлением данных

selection
    .data(newData)
    .transition()
    .duration(1000)
    .attr("height", d => d.value);

При изменении данных диаграмма плавно перестраивается.


Использование .end()

Transition возвращает Promise.

await rect.transition()
    .duration(1000)
    .attr("x", 300)
    .end();

После завершения можно запускать следующий код.


Управление через события

transition.on("start", () => {
    console.log("Старт");
});

transition.on("end", () => {
    console.log("Завершение");
});

Сложный пример последовательной анимации

const bars = svg.selectAll("rect");

bars.transition()
    .delay((d, i) => i * 200)
    .duration(1000)
    .attr("height", d => d.value)
    .transition()
    .duration(500)
    .style("fill", "orange");

Этапы:

  1. столбцы растут;
  2. после завершения меняют цвет.

Использование общего transition

const t = d3.transition()
    .duration(1500)
    .delay(500);

svg.selectAll("circle")
    .transition(t)
    .attr("r", 40);

svg.selectAll("rect")
    .transition(t)
    .attr("width", 120);

Полезно для синхронизации нескольких групп элементов.


Влияние FPS на плавность

D3.js использует requestAnimationFrame.

Обычно браузер стремится поддерживать:

60 FPS

При длинных и тяжёлых анимациях:

  • падает FPS;
  • движение становится дёрганым;
  • возрастает нагрузка на CPU/GPU.

Анимация SVG и DOM

Transition может изменять:

  • SVG-атрибуты;
  • CSS-стили;
  • текст;
  • transform;
  • opacity;
  • размеры HTML-элементов.

Анимация transform

.transition()
.duration(1000)
.attr("transform", "translate(200, 0)");

Комбинирование transform и delay

.transition()
.delay((d, i) => i * 100)
.duration(800)
.attr("transform", (d, i) => {
    return `translate(${i * 80}, 0)`;
});

Внутренний механизм таймеров

D3.js использует:

  • очередь transition;
  • интерполяцию значений;
  • внутренние таймеры;
  • синхронизацию с браузером.

Методы .duration() и .delay() управляют этими таймерами напрямую.


Временная модель transition

Каждый transition проходит стадии:

  1. создание;
  2. ожидание delay;
  3. запуск;
  4. выполнение duration;
  5. завершение.

Визуальный ритм интерфейса

Грамотная настройка времени позволяет:

  • управлять вниманием;
  • подчёркивать изменения;
  • визуально связывать состояния;
  • создавать ощущение плавности.

Именно .duration() и .delay() формируют ритм всей анимационной системы D3.js.