Одной из ключевых особенностей библиотеки D3.js является механизм переходов — система плавного изменения свойств DOM-элементов во времени. Переходы позволяют анимировать графики, визуализировать изменения данных, создавать интерактивные интерфейсы и улучшать восприятие информации.
Основой механизма анимации служит метод
.transition().
.transition()Метод .transition() создаёт объект перехода для
выбранных элементов. После вызова свойства элементов изменяются не
мгновенно, а постепенно.
Простейший пример:
d3.select("rect")
.transition()
.attr("width", 300);
В данном случае ширина прямоугольника изменится плавно.
Без перехода:
d3.select("rect")
.attr("width", 300);
изменение происходит мгновенно.
После вызова .transition() библиотека:
D3 использует интерполяцию значений и синхронизирует обновления через
requestAnimationFrame.
.duration()Определяет длительность анимации в миллисекундах.
d3.select("circle")
.transition()
.duration(2000)
.attr("r", 80);
Радиус круга изменится за 2 секунды.
.delay()Устанавливает задержку перед началом анимации.
d3.select("circle")
.transition()
.delay(1000)
.attr("r", 80);
Переход начнётся через 1 секунду.
.ease()Определяет функцию плавности.
d3.select("circle")
.transition()
.ease(d3.easeBounce)
.attr("cy", 300);
D3 содержит множество встроенных easing-функций.
.ease(d3.easeLinear)
Скорость постоянна на протяжении всего перехода.
.ease(d3.easeQuad)
Плавное ускорение.
.ease(d3.easeCubic)
Более мягкое изменение скорости.
.ease(d3.easeBounce)
Создаёт имитацию прыжка или падения.
.ease(d3.easeElastic)
Добавляет колебания.
Переходы часто используются для изменения SVG-атрибутов.
d3.select("rect")
.transition()
.duration(1000)
.attr("width", 400)
.attr("height", 200);
d3.select("circle")
.transition()
.attr("cx", 500)
.attr("cy", 150);
d3.select("rect")
.transition()
.style("fill", "orange");
D3 автоматически интерполирует цвета.
Внутри переходов работает система интерполяторов.
Например:
.attr("x", 500)
означает постепенное изменение значения от текущего x до
500.
Для цветов:
.style("fill", "red")
D3 вычисляет промежуточные оттенки автоматически.
d3.interpolate(0, 100)(0.5)
Результат:
50
d3.interpolateRgb("red", "blue")(0.5)
Возвращает промежуточный цвет.
D3 умеет извлекать числа даже из строк.
d3.interpolateString(
"translate(0,0)",
"translate(100,50)"
)
Метод .transition() преобразует обычную selection в
transition-selection.
const t = d3.selectAll("rect")
.transition();
После этого доступны методы переходов:
.duration().delay().ease().on()Переходы поддерживают chaining.
d3.select("circle")
.transition()
.duration(1000)
.attr("cx", 300)
.style("fill", "green");
Все изменения выполняются одновременно.
Можно выстраивать анимации последовательно.
d3.select("circle")
.transition()
.duration(1000)
.attr("cx", 300)
.transition()
.duration(1000)
.attr("cy", 200);
Сначала изменится cx, затем cy.
Для независимых анимаций используются разные transition-объекты.
const t1 = d3.transition()
.duration(1000);
const t2 = d3.transition()
.duration(3000);
d3.select("circle")
.transition(t1)
.attr("cx", 300);
d3.select("rect")
.transition(t2)
.attr("width", 500);
D3 поддерживает именование transition.
.transition("move")
Это позволяет управлять конкретными анимациями.
d3.select("circle")
.transition("move")
.attr("cx", 300);
Новый transition с тем же именем прерывает предыдущий.
d3.select("circle")
.transition("move")
.attr("cx", 100);
d3.select("circle")
.transition("move")
.attr("cx", 500);
Второй переход остановит первый.
.interrupt()Позволяет вручную остановить анимацию.
d3.select("circle")
.interrupt();
Transition поддерживает события жизненного цикла.
.transition()
.on("start", function() {
console.log("Старт");
});
.transition()
.on("end", function() {
console.log("Завершение");
});
.transition()
.on("interrupt", function() {
console.log("Прервано");
});
thisВ обработчиках событий this указывает на текущий
DOM-элемент.
.on("end", function() {
d3.select(this)
.style("fill", "red");
});
Особенно важны transition при обновлении визуализации.
svg.selectAll("rect")
.data(data)
.transition()
.attr("height", d => d);
Высота столбцов будет изменяться плавно.
svg.selectAll("rect")
.data(data)
.join("rect")
.transition()
.duration(1000)
.attr("x", (d, i) => i * 50)
.attr("y", d => 300 - d)
.attr("height", d => d)
.attr("width", 40);
Переходы часто применяются к новым элементам.
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("r", 0)
.transition()
.attr("r", 20);
Круги появляются с анимацией увеличения.
Удаление элементов также можно анимировать.
svg.selectAll("circle")
.data(data)
.exit()
.transition()
.attr("r", 0)
.remove();
.remove()Метод .remove() в transition удаляет элемент только
после завершения анимации.
.transition()
.style("opacity", 0)
.remove();
d3.select("text")
.transition()
.style("opacity", 0);
Часто используется для fade-in и fade-out эффектов.
d3.select("text")
.style("opacity", 0)
.transition()
.style("opacity", 1);
Переходы отлично работают с SVG-transform.
.transition()
.attr("transform", "translate(300,100)");
.transition()
.attr("transform", "scale(2)");
.transition()
.attr("transform", "rotate(180)");
Метод .attrTween() позволяет полностью контролировать
процесс интерполяции.
.transition()
.attrTween("cx", function() {
return function(t) {
return 100 + t * 300;
};
});
t изменяется от 0 до 1.
.styleTween()Аналогично работает со стилями.
.transition()
.styleTween("fill", function() {
return d3.interpolateRgb(
"red",
"blue"
);
});
.tween()Создание произвольной анимации.
.transition()
.tween("custom", function() {
return function(t) {
console.log(t);
};
});
Переходы активно используются для анимации линий и кривых.
path.transition()
.attr("d", line(newData));
D3 интерполирует путь между состояниями.
Плавное изменение формы графика:
svg.select("path")
.datum(newData)
.transition()
.duration(1500)
.attr("d", line);
Оси также поддерживают анимацию.
axisGroup
.transition()
.call(d3.axisLeft(scale));
Изменения шкалы отображаются плавно.
Transition особенно полезны при перестройке диаграмм.
bars.transition()
.attr("x", (d, i) => x(i));
Столбцы перемещаются в новые позиции.
Эффект каскадной анимации достигается через функцию в
.delay().
.transition()
.delay((d, i) => i * 100)
.attr("height", d => d);
Элементы запускаются по очереди.
.duration().transition()
.duration((d, i) => i * 200);
Каждый элемент получает собственную длительность.
.transition()
.delay((d, i) => i * 50)
.duration(500)
.ease(d3.easeCubic);
Анимации могут становиться дорогими при большом количестве элементов.
Основные причины снижения производительности:
Лучше изменять transform, чем геометрию элемента.
.attr("transform", ...)
SVG плохо масштабируется на десятки тысяч узлов.
Для сложных анимаций иногда используют Canvas вместо SVG.
Transition не блокируют выполнение кода.
d3.select("circle")
.transition()
.duration(1000);
console.log("Код продолжает работу");
Современные версии D3 позволяют работать с завершением перехода через
.end().
await d3.select("circle")
.transition()
.attr("cx", 300)
.end();
Если transition был остановлен, .end() выбросит
исключение.
try {
await transition.end();
} catch(e) {
console.log("Переход прерван");
}
Внутри D3 transition построены поверх системы таймеров.
d3.timer(callback);
Однако .transition() предоставляет более высокий уровень
абстракции.
Transition особенно эффективны в:
selection
.data(data)
.join(
enter => enter
.append("rect")
.attr("height", 0)
.transition()
.attr("height", d => d),
update => update
.transition()
.attr("height", d => d),
exit => exit
.transition()
.attr("height", 0)
.remove()
);
Эта архитектура лежит в основе большинства динамических визуализаций в D3.