Bouncing эффекты

Работа с физически правдоподобными анимациями в Popmotion строится вокруг моделей движения, которые имитируют поведение реальных объектов: упругость, инерцию, затухание, реакцию на границы. Bouncing-эффекты возникают там, где движение не останавливается резко, а отражается от ограничения с потерей энергии и постепенным затуханием амплитуды.

В Popmotion подобное поведение достигается комбинацией spring-модели, decay-инерции и ручного управления значениями через update-функции. Ключевой принцип — управление скоростью и коэффициентами упругости, а не прямое задание позиции.

Физическая модель отскока

Отскок можно рассматривать как преобразование скорости при столкновении с границей:

  • позиция изменяется по времени
  • скорость уменьшается из-за трения
  • при достижении границы скорость инвертируется
  • часть энергии теряется на коэффициенте restitution

В Popmotion это реализуется через модификацию velocity внутри анимационного цикла.

Базовая реализация bouncing через decay

Decay-модель используется для инерционного движения, которое постепенно затухает. Добавление bounce-логики происходит через контроль пределов значения.

import { decay, animate } from 'popmotion';

animate({
  from: 0,
  to: 1000,
  velocity: 800,
  decay: 0.95,
  onUpdate: (v) => {
    console.log(v);
  }
});

Decay сам по себе не отскакивает, но создаёт основу движения. Для bounce добавляется проверка границ:

let position = 0;
let velocity = 600;
const min = 0;
const max = 300;

function tick() {
  velocity *= 0.98;
  position += velocity * 0.016;

  if (position > max) {
    position = max;
    velocity *= -0.7;
  }

  if (position < min) {
    position = min;
    velocity *= -0.7;
  }

  console.log(position);

  if (Math.abs(velocity) > 0.5) {
    requestAnimationFrame(tick);
  }
}

tick();

Коэффициент -0.7 задаёт упругость столкновения. Чем ближе к -1, тем более «резиновым» выглядит отскок.

Spring как основа bouncing-поведения

Spring-модель в Popmotion — наиболее естественный способ создания bouncing. Она уже содержит параметры упругости и демпфирования.

Ключевые параметры:

  • stiffness — жёсткость пружины
  • damping — затухание
  • mass — масса объекта
import { animate, spring } from 'popmotion';

animate({
  from: 0,
  to: 300,
  type: spring,
  stiffness: 200,
  damping: 12,
  mass: 1,
  onUpdate: (v) => {
    console.log(v);
  }
});

При низком damping система начинает колебаться вокруг цели, создавая эффект bounce без ручной обработки границ.

Колебания вокруг цели

Bounce часто воспринимается не как удар о стену, а как перерегулирование — выход за целевое значение и возврат.

Поведение spring можно усилить:

animate({
  from: 0,
  to: 500,
  type: spring,
  stiffness: 120,
  damping: 6,
  mass: 1,
  onUpdate: v => console.log(v)
});

Здесь возникает серия затухающих колебаний, визуально похожих на отскок мяча после падения.

Имитация физического удара о границы

Для более точного bouncing с ограничениями используется комбинированный подход: spring + clamp + inversion velocity.

let position = 150;
let velocity = 900;

const bounds = { min: 0, max: 300 };
const restitution = 0.75;

function step() {
  velocity *= 0.99;
  position += velocity * 0.016;

  if (position >= bounds.max) {
    position = bounds.max;
    velocity = -velocity * restitution;
  }

  if (position <= bounds.min) {
    position = bounds.min;
    velocity = -velocity * restitution;
  }

  console.log(position);

  if (Math.abs(velocity) > 0.2) {
    requestAnimationFrame(step);
  }
}

step();

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

Bounce через keyframes с физическим характером

Keyframes позволяют задать дискретные точки движения, но bounce достигается через промежуточные контрольные значения.

import { animate, keyframes } from 'popmotion';

animate({
  type: keyframes,
  values: [0, -50, 0, 40, 0],
  duration: 800,
  ease: (t) => t,
  onUpdate: v => console.log(v)
});

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

Overshoot как форма bounce

Overshoot возникает, когда анимация выходит за конечное значение и возвращается обратно.

import { animate, easeOut } from 'popmotion';

animate({
  from: 0,
  to: 300,
  ease: easeOut,
  onUpdate: v => console.log(v)
});

Для усиления эффекта используется кастомная easing-функция:

const overshootEase = t =>
  t < 0.8
    ? (t / 0.8) * 1.2
    : 1 + (1 - t) * 0.2;

animate({
  from: 0,
  to: 400,
  ease: overshootEase,
  onUpdate: v => console.log(v)
});

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

Сочетание spring и constraints

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

import { animate, spring } from 'popmotion';

animate({
  from: 150,
  to: 600,
  type: spring,
  stiffness: 180,
  damping: 14,
  onUpdate: v => {
    const clamped = Math.max(0, Math.min(300, v));
    console.log(clamped);
  }
});

Здесь физическая модель продолжает «жить» за пределами границ, но визуально ограничивается.

Многоосевые bouncing эффекты

Для 2D-движения bounce применяется отдельно по каждой оси.

let x = 100;
let y = 100;

let vx = 500;
let vy = 650;

const bounds = { w: 300, h: 300 };
const restitution = 0.8;

function frame() {
  vx *= 0.99;
  vy *= 0.99;

  x += vx * 0.016;
  y += vy * 0.016;

  if (x > bounds.w || x < 0) {
    vx = -vx * restitution;
    x = Math.max(0, Math.min(bounds.w, x));
  }

  if (y > bounds.h || y < 0) {
    vy = -vy * restitution;
    y = Math.max(0, Math.min(bounds.h, y));
  }

  console.log({ x, y });

  requestAnimationFrame(frame);
}

frame();

Такой подход применяется в интерфейсах drag-and-bounce, где элемент «пружинит» при выходе за контейнер.

Drag + bounce взаимодействие

Bounce особенно заметен при перетаскивании объектов. Popmotion предоставляет pointer-события, которые можно комбинировать с физикой.

import { pointer, animate } from 'popmotion';

let x = 0;
let y = 0;

pointer().start(({ x: px, y: py }) => {
  x = px;
  y = py;

  console.log(x, y);
});

Добавление bounce после отпускания:

animate({
  from: x,
  to: 0,
  type: spring,
  stiffness: 200,
  damping: 10,
  onUpdate: v => {
    x = v;
  }
});

Управление энергией отскока

Bounce определяется сохранением энергии системы:

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

Регулируется через:

  • damping в spring
  • restitution в ручной модели
  • velocity decay

Инверсия скорости как ключевой механизм

Основной математический принцип bounce — изменение знака скорости:

velocity = -velocity * restitution;

Этот шаг превращает любое столкновение в отражение с потерей энергии, создавая реалистичную динамику.

Гибридные системы bounce-анимации

На практике Popmotion-подобные системы используют комбинации:

  • spring для базовой динамики
  • decay для инерции
  • manual tick для контроля границ
  • easing для декоративных эффектов

Такая гибридность позволяет добиться разных типов bouncing:

  • мягкий UI bounce (low damping spring)
  • резкий физический отскок (velocity inversion)
  • декоративный overshoot (easing curves)
  • инерционный bounce при drag (decay + constraints)

Контроль стабильности анимации

При реализации bouncing важно избегать бесконечных колебаний. Обычно применяются пороги остановки:

if (Math.abs(velocity) < 0.1) {
  velocity = 0;
}

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


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