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

В системах анимации функция плавности (easing) определяет, как изменяется прогресс анимации во времени. В Motion One параметр easing влияет на преобразование нормализованного времени t, где:

  • t = 0 — начало анимации
  • t = 1 — завершение анимации

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


Базовая модель функции плавности

Любая функция плавности сводится к преобразованию:

eased = f(t)

где:

  • t — входной прогресс (0..1)
  • f(t) — преобразованный прогресс (0..1 или за его пределами)
  • eased — итоговое значение, применяемое к анимации

Motion One принимает easing как:

  • строку (предустановленные кривые)
  • массив cubic-bezier
  • функцию (t: number) => number

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


Передача пользовательской функции плавности

В Motion One можно передать функцию напрямую:

import { animate } from "motion";

animate(
  ".box",
  { x: 300 },
  {
    duration: 1,
    easing: (t) => t
  }
);

Здесь используется линейная функция, где прогресс не изменяется.


Линейная функция как базовая форма

Линейная интерполяция:

const linear = (t) => t;

Свойства:

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

Квадратичные функции плавности

Квадратичные функции создают более естественное движение за счёт изменения скорости.

Ускорение (ease-in)

const easeInQuad = (t) => t * t;

Поведение:

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

Замедление (ease-out)

const easeOutQuad = (t) => 1 - (1 - t) * (1 - t);

Поведение:

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

Комбинированная кривая (ease-in-out)

const easeInOutQuad = (t) =>
  t < 0.5
    ? 2 * t * t
    : 1 - Math.pow(-2 * t + 2, 2) / 2;

Поведение:

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

Кубические и более сложные функции

Кубические функции обеспечивают более выраженную динамику.

Ease-in cubic

const easeInCubic = (t) => t * t * t;

Ease-out cubic

const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);

Ease-in-out cubic

const easeInOutCubic = (t) =>
  t < 0.5
    ? 4 * t * t * t
    : 1 - Math.pow(-2 * t + 2, 3) / 2;

Кубические кривые:

  • более резкое ускорение
  • более выраженное торможение
  • подходят для UI-движений с акцентом на динамику

Функции с выходом за пределы диапазона

Функции плавности не обязаны оставаться в пределах [0, 1]. Это используется для эффектов переразлёта (overshoot).

const overshoot = (t) => {
  const s = 1.70158;
  return t * t * ((s + 1) * t - s);
};

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

  • значение может превышать 1
  • создаётся эффект “перелёта” цели
  • используется в упругих интерфейсах

Эластичные функции

Эластичное движение моделирует колебания после достижения цели.

const easeOutElastic = (t) => {
  const c4 = (2 * Math.PI) / 3;

  return t === 0
    ? 0
    : t === 1
    ? 1
    : Math.pow(2, -10 * t) *
      Math.sin((t * 10 - 0.75) * c4) +
      1;
};

Свойства:

  • резкий старт
  • серия затухающих колебаний
  • физически похожее поведение пружины

Функции отскока (bounce)

Bounce-эффект моделирует падение и отскок объекта.

const easeOutBounce = (t) => {
  const n1 = 7.5625;
  const d1 = 2.75;

  if (t < 1 / d1) {
    return n1 * t * t;
  } else if (t < 2 / d1) {
    t -= 1.5 / d1;
    return n1 * t * t + 0.75;
  } else if (t < 2.5 / d1) {
    t -= 2.25 / d1;
    return n1 * t * t + 0.9375;
  } else {
    t -= 2.625 / d1;
    return n1 * t * t + 0.984375;
  }
};

Характеристики:

  • несколько касаний “поверхности”
  • убывающая амплитуда
  • имитация гравитации

Интеграция кастомных easing-функций в Motion One

Motion One использует easing на уровне каждого свойства анимации:

animate(
  ".card",
  {
    scale: [0.8, 1],
    opacity: [0, 1]
  },
  {
    duration: 0.6,
    easing: easeOutCubic
  }
);

При этом функция применяется ко всему прогрессу анимации, а не к отдельным ключам.


Составные функции плавности

Сложные кривые часто строятся через композицию простых функций.

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

const scaleTime = (easing) => (t) => easing(t * 2 > 1 ? 1 : t * 2);

Инверсия функции

const invert = (easing) => (t) => 1 - easing(1 - t);

Такие преобразования позволяют:

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

Использование easing в цепочках анимаций

В последовательных анимациях функции плавности влияют на восприятие ритма:

animate(".item-1", { x: 100 }, { easing: easeOutCubic });

animate(".item-2", { x: 100 }, { easing: easeInOutQuad });

animate(".item-3", { x: 100 }, { easing: easeOutElastic });

Разные функции формируют различную визуальную плотность движения:

  • строгие кривые — интерфейсные переходы
  • мягкие кривые — декоративные эффекты
  • упругие кривые — акцентные элементы

Ограничения и особенности кастомных easing-функций

При создании функций плавности учитываются следующие свойства:

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

Оптимальные функции:

  • используют минимальное количество операций
  • избегают тяжёлых тригонометрических вычислений без необходимости
  • не создают побочных эффектов

Практика построения собственных кривых

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

Мягкое ускорение с ограничением скорости

const softStep = (t) => t * t * (3 - 2 * t);

Поведение:

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

Сглаживание сигнала

const smooth = (t) => t * t * (3 - 2 * t);

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

  • UI-переходов
  • микроанимаций
  • стабилизации движения

Построение библиотечных easing-функций

Motion One совместим с функциями, аналогичными CSS cubic-bezier, но расширяет модель до JS-функций.

Пример приближённой кривой CSS:

const cubicBezier = (p0, p1, p2, p3) => {
  return (t) => {
    const u = 1 - t;
    return (
      u * u * u * p0 +
      3 * u * u * t * p1 +
      3 * u * t * t * p2 +
      t * t * t * p3
    );
  };
};

Такая реализация позволяет:

  • создавать собственные редакторы кривых
  • генерировать easing динамически
  • адаптировать анимации под контекст интерфейса