Анимация атрибутов и стилей

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

Главная особенность D3.js заключается в том, что библиотека не создаёт отдельную систему рендеринга. Все изменения происходят напрямую в SVG, HTML или Canvas через изменение свойств элементов.

Базовый синтаксис анимации:

selection.transition()

После вызова transition() все последующие изменения становятся анимируемыми.

Пример:

d3.select("rect")
    .transition()
    .attr("x", 300);

Прямоугольник плавно переместится по оси X к координате 300.


Анимация атрибутов

SVG-элементы содержат множество атрибутов:

  • x
  • y
  • cx
  • cy
  • r
  • width
  • height
  • opacity
  • transform

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

Изменение позиции

svg.select("circle")
    .transition()
    .duration(1000)
    .attr("cx", 500);

Здесь:

  • duration(1000) задаёт длительность анимации в миллисекундах;
  • cx плавно изменяется от текущего значения до 500.

Одновременная анимация нескольких атрибутов

D3.js способен синхронно изменять множество свойств.

svg.select("rect")
    .transition()
    .duration(1500)
    .attr("x", 400)
    .attr("y", 200)
    .attr("width", 300)
    .attr("height", 120);

Все атрибуты изменяются параллельно.


Анимация размеров

Изменение размеров особенно важно для визуализаций данных:

svg.selectAll("rect")
    .data(data)
    .transition()
    .duration(800)
    .attr("height", d => d.value * 10);

Так строятся анимированные столбчатые диаграммы.


Анимация радиуса окружности

svg.select("circle")
    .transition()
    .duration(1200)
    .attr("r", 80);

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


Анимация прозрачности

Прозрачность регулируется через атрибут opacity.

svg.select("text")
    .transition()
    .duration(600)
    .attr("opacity", 0);

Элемент плавно исчезнет.


Анимация стилей

Кроме SVG-атрибутов D3.js умеет анимировать CSS-свойства.

Для этого используется метод style().

Изменение цвета

svg.select("circle")
    .transition()
    .duration(1000)
    .style("fill", "red");

D3 интерполирует цвета автоматически.


Интерполяция цветов

Библиотека поддерживает:

  • HEX
  • RGB
  • RGBA
  • HSL
  • именованные цвета

Пример:

svg.select("rect")
    .transition()
    .style("fill", "#00ff00");

Если начальный цвет был #ff0000, произойдёт плавный переход между оттенками.


Анимация CSS-свойств

d3.select("div")
    .transition()
    .style("width", "400px")
    .style("background-color", "steelblue");

D3 может анимировать HTML-элементы так же эффективно, как SVG.


Управление длительностью

Метод duration()

.transition()
.duration(2000)

Указывает длительность анимации.

Короткая анимация

.duration(300)

Используется для интерфейсных эффектов.

Длинная анимация

.duration(5000)

Подходит для демонстрационных визуализаций.


Задержка анимации

Метод delay()

.transition()
.delay(1000)

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


Каскадная анимация

Часто применяется при отображении списков или диаграмм.

svg.selectAll("circle")
    .data(data)
    .transition()
    .delay((d, i) => i * 100)
    .attr("cy", 200);

Каждый следующий элемент начинает движение позже предыдущего.


Функции плавности

По умолчанию используется easing-функция cubic.

D3 предоставляет большой набор алгоритмов плавности.

easeLinear

Равномерная скорость.

.transition()
.ease(d3.easeLinear)

easeBounce

Эффект отскока.

.transition()
.ease(d3.easeBounce)

easeElastic

Эффект пружины.

.transition()
.ease(d3.easeElastic)

easeCircle

Плавное ускорение и замедление.

.transition()
.ease(d3.easeCircle)

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

Переходы можно объединять в цепочки.

svg.select("circle")
    .transition()
    .duration(1000)
    .attr("cx", 400)
    .transition()
    .duration(1000)
    .attr("cy", 300);

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


Параллельные переходы

Если требуется одновременное выполнение нескольких переходов:

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

svg.select("circle")
    .transition(t)
    .attr("cx", 300);

svg.select("rect")
    .transition(t)
    .attr("x", 500);

Обе анимации синхронизированы.


Анимация transform

SVG-трансформации часто используются вместо изменения координат.

Перемещение

svg.select("rect")
    .transition()
    .attr("transform", "translate(300, 100)");

Масштабирование

svg.select("circle")
    .transition()
    .attr("transform", "scale(2)");

Поворот

svg.select("rect")
    .transition()
    .attr("transform", "rotate(45)");

Интерполяция transform

D3 умеет корректно интерполировать:

  • translate
  • rotate
  • scale
  • skew

Пример:

svg.select("g")
    .transition()
    .attr("transform", "translate(300,100) rotate(90)");

Библиотека автоматически вычислит промежуточные состояния.


Пользовательские интерполяторы

Иногда стандартной интерполяции недостаточно.

attrTween()

svg.select("circle")
    .transition()
    .attrTween("cx", function() {
        return d3.interpolateNumber(0, 500);
    });

styleTween()

d3.select("div")
    .transition()
    .styleTween("background-color", function() {
        return d3.interpolateRgb("blue", "orange");
    });

Интерполяторы D3.js

D3 предоставляет множество встроенных интерполяторов.

interpolateNumber

d3.interpolateNumber(0, 100)

interpolateRgb

d3.interpolateRgb("red", "green")

interpolateString

d3.interpolateString("10px", "100px")

interpolateDate

d3.interpolateDate(
    new Date(2020, 0, 1),
    new Date(2025, 0, 1)
)

Анимация текста

Изменение прозрачности текста

svg.select("text")
    .transition()
    .style("opacity", 0.2);

Изменение размера шрифта

svg.select("text")
    .transition()
    .style("font-size", "40px");

Анимация при обновлении данных

Одно из ключевых преимуществ D3 — анимированное обновление визуализации.

svg.selectAll("rect")
    .data(newData)
    .transition()
    .duration(1000)
    .attr("height", d => d * 5);

Столбцы плавно изменят размеры при изменении массива данных.


Enter-анимация

При добавлении новых элементов:

svg.selectAll("circle")
    .data(data)
    .enter()
    .append("circle")
    .attr("r", 0)
    .transition()
    .duration(800)
    .attr("r", 30);

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


Exit-анимация

При удалении элементов:

svg.selectAll("circle")
    .data(data)
    .exit()
    .transition()
    .duration(500)
    .attr("opacity", 0)
    .remove();

Объекты сначала исчезают, затем удаляются из DOM.


Анимация по событию

Переходы часто запускаются через пользовательские действия.

Наведение мыши

svg.selectAll("circle")
    .on("mouseover", function() {
        d3.select(this)
            .transition()
            .attr("r", 50);
    });

Уход курсора

.on("mouseout", function() {
    d3.select(this)
        .transition()
        .attr("r", 20);
});

Прерывание анимации

interrupt()

d3.select("circle")
    .interrupt();

Текущий переход будет остановлен.


Именованные переходы

D3 поддерживает именование анимаций.

.transition("move")

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


События переходов

start

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

end

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

interrupt

.transition()
.on("interrupt", () => {
    console.log("Прервано");
});

Анимация сложных SVG-форм

Линии

path.transition()
    .attr("d", newLine);

Области

area.transition()
    .attr("d", newArea);

Дуговые сегменты

arc.transition()
    .attrTween("d", arcTween);

Анимация графиков

Плавное обновление осей

xAxis.transition()
    .duration(1000)
    .call(d3.axisBottom(scale));

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

svg.selectAll("rect")
    .data(data)
    .transition()
    .delay((d, i) => i * 50)
    .duration(500)
    .attr("y", d => height - d)
    .attr("height", d => d);

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

При работе с большими объёмами данных необходимо учитывать нагрузку на браузер.

Основные рекомендации

Минимизировать количество DOM-элементов

SVG плохо масштабируется при десятках тысяч объектов.


Избегать тяжёлых теней и фильтров

CSS-фильтры могут значительно снижать FPS.


Использовать transform вместо x/y

Трансформации обычно быстрее, поскольку активируют GPU-ускорение.


Сокращать количество одновременно анимируемых элементов

Большое число параллельных переходов перегружает поток рендеринга.


Частые ошибки

Конфликт переходов

selection
    .transition()
    .attr("x", 100);

selection
    .transition()
    .attr("x", 300);

Второй переход прервёт первый.


Отсутствие начального состояния

.attr("opacity", 0)
.transition()
.attr("opacity", 1)

Без начального состояния эффект появления может отсутствовать.


Некорректная интерполяция строк

.attr("width", "100%")

Некоторые строковые значения D3 не способен корректно интерполировать.


Архитектура анимаций

В крупных проектах рекомендуется:

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

Пример комплексной анимации

const transition = d3.transition()
    .duration(1200)
    .ease(d3.easeCubicInOut);

svg.selectAll("circle")
    .data(data)
    .join("circle")
    .attr("cx", d => d.x)
    .attr("cy", d => d.y)
    .attr("r", 0)
    .style("fill", "steelblue")
    .transition(transition)
    .delay((d, i) => i * 80)
    .attr("r", d => d.radius)
    .style("fill", "orange");

В этом примере используются:

  • общий transition;
  • задержка;
  • анимация атрибутов;
  • анимация стилей;
  • интерполяция радиуса;
  • последовательное появление элементов.