Модуль d3-interpolate предназначен для вычисления
промежуточных значений между двумя состояниями. Интерполяция лежит в
основе анимаций, плавных переходов, морфинга фигур, изменения цветов,
шкалирования и обновления визуализаций.
В экосистеме D3 интерполяция используется практически везде:
d3-transition);Главная идея библиотеки — получение функции-интерполятора.
const interpolate = d3.interpolate(a, b)
После этого функция принимает значение от 0 до
1:
interpolate(t)
где:
0 — начальное состояние;1 — конечное состояние;const i = d3.interpolate(10, 50)
console.log(i(0)) // 10
console.log(i(0.5)) // 30
console.log(i(1)) // 50
Функция автоматически определяет тип данных и выбирает подходящий механизм интерполяции.
Формула линейной интерполяции:
v(t)=a+(b-a)t
где:
a — начальное значение;b — конечное значение;t — коэффициент интерполяции от 0 до
1.const interpolateNumber = d3.interpolateNumber(0, 100)
console.log(interpolateNumber(0.25))
console.log(interpolateNumber(0.75))
Результат:
25
75
Этот метод используется:
Библиотека умеет извлекать числа из строк и плавно изменять их.
const i = d3.interpolateString(
"translate(0, 0)",
"translate(200, 100)"
)
console.log(i(0.5))
Результат:
translate(100, 50)
Это особенно важно для SVG-трансформаций.
const color = d3.interpolateRgb("red", "blue")
console.log(color(0))
console.log(color(0.5))
console.log(color(1))
Интерполяция происходит в пространстве RGB.
const color = d3.interpolateHsl("yellow", "green")
Интерполяция в HSL даёт более естественные переходы оттенков.
const color = d3.interpolateLab("#ff0000", "#0000ff")
Цветовое пространство LAB лучше подходит для визуально равномерных переходов.
const color = d3.interpolateCubehelix(
"purple",
"orange"
)
Используется в научной визуализации и тепловых картах.
Интерполяция часто применяется совместно с последовательными шкалами.
const scale = d3.scaleSequential()
.domain([0, 100])
.interpolator(d3.interpolateViridis)
Теперь каждое число преобразуется в цвет.
console.log(scale(10))
console.log(scale(90))
const i = d3.interpolateArray(
[0, 10, 20],
[100, 200, 300]
)
console.log(i(0.5))
Результат:
[50, 105, 160]
Каждый элемент массива интерполируется независимо.
const i = d3.interpolateObject(
{ x: 0, y: 0 },
{ x: 100, y: 200 }
)
console.log(i(0.5))
Результат:
{
x: 50,
y: 100
}
Очень полезно для:
const start = new Date(2024, 0, 1)
const end = new Date(2024, 11, 31)
const i = d3.interpolateDate(start, end)
console.log(i(0.5))
Возвращается промежуточная дата.
const i = d3.interpolateTransformSvg(
"translate(0,0) scale(1)",
"translate(300,100) scale(2)"
)
console.log(i(0.5))
Результат:
translate(150,50) scale(1.5)
Метод умеет корректно разбирать:
translate;scale;rotate;skewX;matrix.const i = d3.interpolateTransformCss(
"translateX(0px)",
"translateX(400px)"
)
Используется для HTML-элементов.
Вместо плавного изменения выбираются значения из набора.
const i = d3.interpolateDiscrete([
"small",
"medium",
"large"
])
console.log(i(0.2))
console.log(i(0.8))
Подходит для:
Метод создаёт массив значений интерполяции.
const values = d3.quantize(
d3.interpolateNumber(0, 1),
5
)
console.log(values)
Результат:
[0, 0.25, 0.5, 0.75, 1]
Часто используется:
Наиболее часто d3-interpolate используется внутри
переходов.
d3.select("circle")
.transition()
.duration(1000)
.attr("cx", 300)
D3 автоматически создаёт интерполятор между старым и новым значением.
d3.select("circle")
.transition()
.attrTween("cx", function() {
const i = d3.interpolateNumber(0, 300)
return function(t) {
return i(t)
}
})
Здесь интерполяция создаётся вручную.
d3.select("div")
.transition()
.styleTween("background-color", function() {
const i = d3.interpolateRgb(
"red",
"blue"
)
return function(t) {
return i(t)
}
})
Одно из самых сложных направлений.
const path1 = "M0,0L100,0L100,100"
const path2 = "M0,0L200,50L100,200"
const i = d3.interpolateString(path1, path2)
Однако корректный морфинг работает только при совместимой структуре путей.
Проблемы возникают, если:
Для сложного морфинга часто используются:
flubber;d3-interpolate-path.Интерполяция отвечает за вычисление значений, а easing — за характер движения.
Без easing:
t = 0 → 1
С easing:
ease(t)
Пример:
d3.select("rect")
.transition()
.ease(d3.easeBounce)
.attr("x", 400)
Процесс:
function customInterpolator(a, b) {
return function(t) {
return a + Math.sin(t) * (b - a)
}
}
Использование:
const i = customInterpolator(0, 100)
console.log(i(0.5))
D3 умеет рекурсивно обрабатывать вложенные объекты.
const i = d3.interpolate(
{
position: { x: 0, y: 0 },
color: "red"
},
{
position: { x: 100, y: 200 },
color: "blue"
}
)
Внутри будут автоматически созданы:
При больших объёмах данных важны:
Плохо:
for (let i = 0; i < 10000; i++) {
d3.interpolateNumber(0, 100)
}
Лучше:
const interpolate = d3.interpolateNumber(0, 100)
for (let i = 0; i < 10000; i++) {
interpolate(Math.random())
}
Интерполяция обычно быстрая. Основные затраты происходят при:
Многие шкалы D3 используют интерполяцию внутри.
const scale = d3.scaleLinear()
.domain([0, 100])
.range([0, 500])
Фактически шкала выполняет:
genui{“math_block_widget_always_prefetch_v2”:{“content”:“y=r_0+(r_1-r_0)”}}
где:
d0, d1 — домен;r0, r1 — диапазон.d3-interpolate не зависит от SVG и DOM.
const interpolateX = d3.interpolateNumber(0, 500)
function render(t) {
const x = interpolateX(t)
ctx.clearRect(0, 0, width, height)
ctx.beginPath()
ctx.arc(x, 100, 20, 0, Math.PI * 2)
ctx.fill()
}
D3-интерполяторы можно использовать вне D3-анимаций.
const i = d3.interpolateNumber(0, 300)
const start = performance.now()
function animate(time) {
const elapsed = time - start
const t = Math.min(elapsed / 1000, 1)
const value = i(t)
console.log(value)
if (t < 1) {
requestAnimationFrame(animate)
}
}
requestAnimationFrame(animate)
При обновлении данных D3 автоматически интерполирует значения.
selection
.data(data)
.transition()
.attr("height", d => d.value)
Старые значения плавно переходят в новые.
| Интерполятор | Назначение |
|---|---|
interpolateNumber |
Числа |
interpolateRgb |
RGB-цвета |
interpolateHsl |
HSL-цвета |
interpolateLab |
LAB-цвета |
interpolateString |
Строки |
interpolateArray |
Массивы |
interpolateObject |
Объекты |
interpolateDate |
Даты |
interpolateTransformSvg |
SVG-transform |
interpolateTransformCss |
CSS-transform |
interpolateDiscrete |
Дискретные значения |
const svg = d3.select("svg")
svg.append("circle")
.attr("cx", 50)
.attr("cy", 100)
.attr("r", 20)
.transition()
.duration(2000)
.ease(d3.easeCubic)
.attr("cx", 400)
.attr("fill", "orange")
Внутри D3 создаёт:
Полный процесс выглядит так:
(t) => value.t.Метод d3.interpolate() самостоятельно определяет
типы.
d3.interpolate(0, 100)
d3.interpolate("red", "blue")
d3.interpolate([1,2], [5,6])
d3.interpolate({x:0}, {x:100})
Это делает API чрезвычайно гибким.
Интерполяция не учитывает:
Для этого используется:
d3-force;Проблемы возникают при:
d3.interpolate(
[1,2,3],
[1,2]
)
или:
d3.interpolate(
{x:10},
{y:20}
)
Результаты могут быть непредсказуемыми.
Использует интерполяцию для анимаций.
Использует интерполяцию для преобразования значений.
Предоставляет цветовые пространства.
Изменяет временную функцию интерполяции.
Интерполяция позволяет описывать только:
Все промежуточные кадры вычисляются автоматически.
Именно это делает D3 мощным инструментом анимированной визуализации данных.