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

Интерполяция цветов в D3.js используется для плавного перехода между двумя или несколькими цветовыми значениями. Механизм интерполяции лежит в основе анимаций, градиентов, тепловых карт, цветовых шкал и визуализации данных, где цвет отражает изменение состояния или числового диапазона.

В библиотеке D3.js интерполяция реализована через набор функций семейства d3.interpolate*, каждая из которых работает с определённой цветовой моделью.

Базовый принцип работы интерполятора:

const interpolate = d3.interpolateRgb("red", "blue");

console.log(interpolate(0));   // red
console.log(interpolate(0.5)); // промежуточный цвет
console.log(interpolate(1));   // blue

Аргумент функции интерполятора принимает значение от 0 до 1:

  • 0 — начальный цвет
  • 1 — конечный цвет
  • промежуточные значения — промежуточные оттенки

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

Функция d3.interpolateRgb

Наиболее распространённый способ интерполяции цветов.

const color = d3.interpolateRgb("#ff0000", "#0000ff");

console.log(color(0.25));
console.log(color(0.5));
console.log(color(0.75));

D3 выполняет линейную интерполяцию отдельно для каждого канала:

  • Red
  • Green
  • Blue

Например:

rgb(255, 0, 0) → rgb(0, 0, 255)

Промежуточные значения вычисляются математически.

Использование в SVG

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

for (let i = 0; i <= 10; i++) {
    svg.append("rect")
        .attr("x", i * 40)
        .attr("y", 0)
        .attr("width", 40)
        .attr("height", 40)
        .attr("fill", d3.interpolateRgb("orange", "purple")(i / 10));
}

Результатом станет плавный цветовой переход.


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

Функция d3.interpolateHsl

RGB-интерполяция не всегда создаёт визуально естественные переходы. Цвет может становиться грязным или тусклым. Для более плавных переходов используется модель HSL.

const color = d3.interpolateHsl("yellow", "blue");

console.log(color(0.5));

HSL состоит из:

  • Hue — оттенок
  • Saturation — насыщенность
  • Lightness — яркость

Отличия RGB и HSL

RGB:

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

HSL:

d3.interpolateHsl("red", "green")(0.5)

Результаты будут визуально различаться:

  • RGB создаёт математический переход
  • HSL создаёт perceptual-переход, ближе к человеческому восприятию

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

Функция d3.interpolateLab

Цветовая модель LAB разработана для более точного соответствия восприятию человека.

const color = d3.interpolateLab("white", "steelblue");

console.log(color(0.5));

Преимущества LAB:

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

LAB особенно полезен:

  • в тепловых картах
  • научной визуализации
  • аналитических дашбордах

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

Функция d3.interpolateHcl

HCL является модификацией LAB.

const color = d3.interpolateHcl("purple", "orange");

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

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

HCL часто считается лучшим вариантом для визуализации данных.


Cubehelix-интерполяция

Функция d3.interpolateCubehelix

Алгоритм Cubehelix создаёт цветовые схемы с равномерным изменением яркости.

const color = d3.interpolateCubehelix("purple", "orange");

Преимущества:

  • хорошая читаемость в grayscale
  • корректное отображение при печати
  • равномерная яркость

Используется в:

  • научной графике
  • публикациях
  • картах плотности

Цветовые пространства и визуальное восприятие

Сравнение моделей

Модель Особенность Применение
RGB Простая математическая интерполяция Базовые анимации
HSL Более плавные оттенки UI, декоративные эффекты
LAB Перцептивная равномерность Аналитика
HCL Улучшенная визуализация Data Visualization
Cubehelix Равномерная яркость Научные графики

Интерполяция и шкалы

Интерполяция тесно связана со шкалами (scales).

Линейная цветовая шкала

const scale = d3.scaleLinear()
    .domain([0, 100])
    .range(["white", "darkblue"]);

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

console.log(scale(50));

Явное указание интерполятора

Метод .interpolate() позволяет выбрать модель интерполяции.

const scale = d3.scaleLinear()
    .domain([0, 100])
    .range(["red", "blue"])
    .interpolate(d3.interpolateHcl);

Теперь шкала использует HCL.


Непрерывные цветовые схемы

Последовательные шкалы

const color = d3.scaleSequential(d3.interpolateViridis)
    .domain([0, 100]);

Использование:

rect.attr("fill", d => color(d.value));

Готовые интерполяторы D3

D3 содержит встроенные цветовые схемы.

Viridis

d3.interpolateViridis(0.5)

Inferno

d3.interpolateInferno(0.5)

Plasma

d3.interpolatePlasma(0.5)

Magma

d3.interpolateMagma(0.5)

Cividis

d3.interpolateCividis(0.5)

Визуализация встроенных схем

const schemes = [
    d3.interpolateViridis,
    d3.interpolateInferno,
    d3.interpolatePlasma
];

schemes.forEach((scheme, row) => {
    for (let i = 0; i < 100; i++) {
        svg.append("rect")
            .attr("x", i * 5)
            .attr("y", row * 30)
            .attr("width", 5)
            .attr("height", 30)
            .attr("fill", scheme(i / 99));
    }
});

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

D3 активно использует интерполяцию при переходах (transitions).

Анимация изменения цвета

d3.select("rect")
    .transition()
    .duration(2000)
    .attr("fill", "green");

Во время transition D3 автоматически создаёт интерполятор.


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

Создание собственной функции

function customInterpolator(t) {
    return `rgb(${255 * t}, 0, ${255 * (1 - t)})`;
}

Использование:

svg.append("rect")
    .attr("fill", customInterpolator(0.3));

Интерполяция нескольких цветов

Многоступенчатая шкала

const scale = d3.scaleLinear()
    .domain([0, 50, 100])
    .range(["green", "yellow", "red"]);

D3 автоматически создаёт сегментированную интерполяцию.


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

Heatmap

const color = d3.scaleSequential()
    .domain([0, 1])
    .interpolator(d3.interpolateInferno);

Применение:

cells.attr("fill", d => color(d.temperature));

Diverging-интерполяция

Используется для данных с центральной точкой.

const color = d3.scaleDiverging(d3.interpolateRdYlBu)
    .domain([-1, 0, 1]);

Типичные сценарии:

  • отклонения
  • финансовые изменения
  • температурные аномалии

Категориальные и непрерывные цвета

Непрерывные шкалы

d3.scaleSequential()

Предназначены для:

  • числовых диапазонов
  • плотностей
  • интенсивностей

Категориальные шкалы

d3.scaleOrdinal()

Используются для:

  • групп
  • категорий
  • дискретных классов

Цветовые схемы ColorBrewer

D3 поддерживает палитры ColorBrewer.

d3.schemeBlues[9]

Пример:

const scale = d3.scaleQuantize()
    .domain([0, 100])
    .range(d3.schemeBlues[9]);

Интерполяция прозрачности

D3 интерполирует и alpha-канал.

d3.interpolateRgb(
    "rgba(255,0,0,0.1)",
    "rgba(255,0,0,1)"
)(0.5);

Интерполяция объектов цвета

Объекты d3.color

const c1 = d3.color("red");
const c2 = d3.color("blue");

const interpolate = d3.interpolate(c1, c2);

Универсальная функция d3.interpolate

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

d3.interpolate("red", "blue");

Также поддерживаются:

  • числа
  • массивы
  • объекты
  • даты
  • строки

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

const interpolate = d3.interpolateArray(
    ["red", "green"],
    ["blue", "yellow"]
);

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

const interpolate = d3.interpolateObject(
    { color: "red", value: 10 },
    { color: "blue", value: 100 }
);

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

При работе с большими наборами данных важно учитывать:

  • количество DOM-элементов
  • частоту обновления
  • сложность цветовых вычислений

Рекомендации

Использование Canvas

const context = canvas.getContext("2d");

Canvas эффективнее SVG при десятках тысяч элементов.

Кэширование интерполяторов

Плохо:

.attr("fill", d => d3.interpolateViridis(d))

Лучше:

const viridis = d3.interpolateViridis;

.attr("fill", d => viridis(d))

Перцептивно равномерные схемы

Наиболее качественными считаются:

  • Viridis
  • Inferno
  • Plasma
  • Cividis

Причины:

  • одинаковое восприятие шагов
  • корректная яркость
  • доступность для дальтоников

Работа с scaleSequential

Полный пример

const color = d3.scaleSequential()
    .domain([0, 100])
    .interpolator(d3.interpolateViridis);

svg.selectAll("circle")
    .data(data)
    .enter()
    .append("circle")
    .attr("cx", d => d.x)
    .attr("cy", d => d.y)
    .attr("r", 5)
    .attr("fill", d => color(d.value));

Инвертирование цветовых схем

const color = d3.scaleSequential(t =>
    d3.interpolateViridis(1 - t)
);

Дискретизация непрерывной шкалы

const scale = d3.scaleQuantize()
    .domain([0, 1])
    .range(d3.schemeGreens[5]);

Интерполяция и легенды

Цветовые легенды обычно строятся через непрерывные шкалы.

for (let i = 0; i < 100; i++) {
    legend.append("rect")
        .attr("x", i)
        .attr("width", 1)
        .attr("height", 20)
        .attr("fill", d3.interpolateViridis(i / 99));
}

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

const gradient = defs.append("radialGradient")
    .attr("id", "grad");

gradient.append("stop")
    .attr("offset", "0%")
    .attr("stop-color", "white");

gradient.append("stop")
    .attr("offset", "100%")
    .attr("stop-color", "blue");

Анимация через attrTween

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

selection.transition()
    .attrTween("fill", function() {

        const interpolate = d3.interpolateRgb(
            "red",
            "blue"
        );

        return function(t) {
            return interpolate(t);
        };
    });

Ошибки при интерполяции цветов

Использование неподходящей модели

RGB может давать:

  • серые промежуточные оттенки
  • потерю насыщенности
  • неестественные переходы

Для аналитики предпочтительнее:

  • LAB
  • HCL
  • Viridis

Некорректный диапазон

Ошибка:

color(200)

при ожидаемом диапазоне 0–1.


Практические сценарии

Карта температуры

const temperature = d3.scaleSequential()
    .domain([-30, 50])
    .interpolator(d3.interpolateTurbo);

Визуализация плотности

const density = d3.scaleSequential()
    .interpolator(d3.interpolateMagma);

Финансовые показатели

const profit = d3.scaleDiverging()
    .interpolator(d3.interpolateRdBu)
    .domain([-100, 0, 100]);

Совместимость с CSS

Интерполяторы D3 возвращают CSS-совместимые строки.

element.style.background =
    d3.interpolateRainbow(0.4);

Интерполяция радуги

d3.interpolateRainbow(t)

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

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

Turbo-интерполяция

d3.interpolateTurbo(t)

Turbo отличается:

  • высокой контрастностью
  • насыщенностью
  • хорошей детализацией

Используется:

  • в scientific visualization
  • машинном обучении
  • heatmaps

Влияние интерполяции на восприятие данных

Неправильно выбранная палитра может:

  • скрывать различия
  • искажать интенсивность
  • вводить в заблуждение

Корректная интерполяция обеспечивает:

  • читаемость
  • контраст
  • визуальную непрерывность
  • адекватное восприятие значений