Interpolate для маппинга диапазонов

Функция интерполяции в Popmotion используется для преобразования значения из одного числового диапазона в другой с возможностью нелинейного распределения, ограничения выходных значений и применения различных стратегий поведения за пределами диапазона.

Механизм interpolate является базовым строительным блоком для анимаций, так как позволяет связывать прогресс анимации (обычно от 0 до 1) с любыми прикладными значениями: координатами, углами, прозрачностью, цветами и сложными шкалами.


Базовая идея интерполяции

Интерполяция — это отображение входного диапазона на выходной:

  • вход: нормализованное значение (например, 0 → 1)
  • выход: произвольный диапазон (например, 0 → 100)

Линейное преобразование можно выразить как:

output = (input - inputMin) / (inputMax - inputMin) * (outputMax - outputMin) + outputMin

В Popmotion эта логика инкапсулирована и расширена дополнительными возможностями.


Простейший пример interpolate

import { interpolate } from '@popmotion/popcorn';

const mapper = interpolate([0, 1], [0, 100]);

mapper(0);   // 0
mapper(0.5); // 50
mapper(1);   // 100

Первый массив задаёт входной диапазон, второй — выходной.


Использование с анимационным прогрессом

Интерполяция чаще всего применяется вместе с прогрессом анимации:

import { animate, interpolate } from 'popmotion';

const scaleMap = interpolate([0, 1], [0.5, 1.5]);

animate({
  from: 0,
  to: 1,
  duration: 1000,
  onUpdate: (v) => {
    const scale = scaleMap(v);
    element.style.transform = `scale(${scale})`;
  }
});

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


Несколько сегментов диапазона

Интерполяция поддерживает разбиение на несколько контрольных точек.

const colorMap = interpolate(
  [0, 0.5, 1],
  ['#ff0000', '#00ff00', '#0000ff']
);

Такое поведение создаёт плавный переход через промежуточные значения.


Нелинейные переходы

Помимо линейного соответствия, можно задавать кривые сглаживания через easing.

import { easeInOut } from '@popmotion/easing';

const smoothMap = interpolate(
  [0, 1],
  [0, 100],
  { ease: easeInOut }
);

Easing изменяет распределение значений внутри диапазона, не меняя конечные точки.


Обработка выходов за границы диапазона

Одной из ключевых особенностей является поведение при выходе входного значения за пределы диапазона.

const mapper = interpolate([0, 1], [0, 100], {
  clamp: false
});

mapper(-0.5); // -50
mapper(1.5);  // 150

Если включить ограничение:

const mapper = interpolate([0, 1], [0, 100], {
  clamp: true
});

mapper(-0.5); // 0
mapper(1.5);  // 100

Clamp используется в UI-анимациях для предотвращения «вылета» значений.


Маппинг сложных UI параметров

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

Прозрачность и движение

const opacityMap = interpolate([0, 1], [0, 1]);
const yMap = interpolate([0, 1], [50, 0]);

onUpdate(v => {
  element.style.opacity = opacityMap(v);
  element.style.transform = `translateY(${yMap(v)}px)`;
});

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

const rotateMap = interpolate([0, 1], [0, 360]);
const scaleMap = interpolate([0, 1], [1, 2]);

element.style.transform = `
  rotate(${rotateMap(progress)}deg)
  scale(${scaleMap(progress)})
`;

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

Для работы с цветами используется преобразование между цветовыми пространствами.

const colorMap = interpolate(
  [0, 1],
  ['rgb(255, 0, 0)', 'rgb(0, 0, 255)']
);

element.style.background = colorMap(progress);

Внутренне Popmotion преобразует цвета в числовые каналы и интерполирует каждый компонент отдельно.


Множественные ключевые точки

При сложных анимациях удобно использовать несколько сегментов:

const map = interpolate(
  [0, 0.3, 0.6, 1],
  [0, 100, 50, 200]
);

Такой подход позволяет моделировать:

  • резкие ускорения
  • возвраты назад
  • нелинейные траектории

Использование в связке с жестами

Интерполяция часто применяется для преобразования жестов в UI изменения.

const dragToScale = interpolate(
  [0, 300],
  [1, 1.8],
  { clamp: true }
);

pointerTracker(({ x }) => {
  element.style.transform = `scale(${dragToScale(x)})`;
});

Обратное преобразование

Иногда требуется обратный маппинг — восстановление исходного диапазона.

const inverse = interpolate([0, 100], [0, 1]);

inverse(50); // 0.5

Это используется при синхронизации состояния интерфейса с анимацией.


Комбинирование нескольких интерполяций

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

const progressMap = interpolate([0, 1], [0, 100]);
const opacityMap = interpolate([0, 100], [1, 0]);

const opacity = opacityMap(progressMap(t));

Такой подход позволяет строить цепочки трансформаций без усложнения основной логики.


Типичные ошибки при использовании

Неправильное выравнивание диапазонов:

interpolate([0, 100], [0, 1])(0.5); // ошибка логики ожидания

Использование без нормализации входных данных приводит к непредсказуемым результатам.


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

Создание интерполятора — операция, которую выгодно выполнять один раз:

const map = interpolate([0, 1], [0, 100]);

// повторное использование
map(0.1);
map(0.2);

Создание функции внутри каждого кадра анимации увеличивает нагрузку на GC и снижает плавность UI.


Роль interpolate в архитектуре анимаций

Интерполяция выступает промежуточным слоем между:

  • источником данных (жест, таймер, скролл)
  • и визуальным состоянием (CSS, SVG, canvas)

Она отделяет математическое преобразование от логики управления состоянием, позволяя строить предсказуемые анимационные системы без привязки к конкретной платформе рендеринга.