Метки времени

Метки времени (timestamps) — это числовые значения, отражающие конкретный момент времени в процессе анимации. В контексте библиотеки Popmotion они играют ключевую роль в управлении ходом анимации, синхронизации событий и вычислении промежуточных состояний.

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


Роль меток времени в Popmotion

В Popmotion метки времени используются для:

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

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


Источник меток времени

Основным источником времени в браузере является метод:

performance.now()

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

Также часто применяется:

requestAnimationFrame((timestamp) => {
  // timestamp — метка времени текущего кадра
})

Этот timestamp автоматически передаётся браузером и синхронизирован с частотой обновления экрана.


Использование времени в анимациях

Рассмотрим пример простой анимации с использованием Popmotion:

import { animate } from "popmotion";

animate({
  from: 0,
  to: 100,
  duration: 1000,
  onUpdate: (latest) => {
    console.log(latest);
  }
});

Внутри animate происходит:

  1. Захват начальной метки времени

  2. На каждом кадре вычисление разницы:

    elapsed = currentTime - startTime
  3. Определение прогресса:

    progress = elapsed / duration
  4. Интерполяция значения


Прогресс анимации и время

Прогресс — это нормализованное значение от 0 до 1, вычисляемое на основе времени:

progress = elapsed / duration

Где:

  • elapsed — прошедшее время
  • duration — длительность анимации

Popmotion автоматически ограничивает прогресс в диапазоне [0, 1].


Управление временем вручную

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

Пример с ручным управлением:

import { animate } from "popmotion";

let startTime;

animate({
  from: 0,
  to: 100,
  duration: 1000,
  driver: (update) => {
    startTime = performance.now();

    function frame() {
      const currentTime = performance.now();
      update(currentTime);

      requestAnimationFrame(frame);
    }

    requestAnimationFrame(frame);

    return () => {};
  }
});

В этом примере:

  • update получает текущую метку времени
  • логика анимации полностью контролируется вручную

Синхронизация нескольких анимаций

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

import { animate } from "popmotion";

const start = performance.now();

const options = {
  duration: 1000,
  driver: (update) => {
    function frame() {
      update(performance.now() - start);
      requestAnimationFrame(frame);
    }
    requestAnimationFrame(frame);
    return () => {};
  }
};

animate({
  ...options,
  from: 0,
  to: 100,
  onUpdate: v => console.log("A:", v)
});

animate({
  ...options,
  from: 100,
  to: 200,
  onUpdate: v => console.log("B:", v)
});

Обе анимации используют одну и ту же временную базу (start), что обеспечивает их синхронность.


Замедление и ускорение времени

Метки времени можно масштабировать для изменения скорости анимации:

const speed = 0.5; // замедление в 2 раза

const elapsed = (currentTime - startTime) * speed;
  • speed < 1 — замедление
  • speed > 1 — ускорение

Это позволяет реализовать эффекты:

  • slow motion
  • ускоренная перемотка

Перемотка и управление прогрессом

Использование времени даёт возможность реализовать перемотку:

let manualTime = 0;

animate({
  from: 0,
  to: 100,
  duration: 1000,
  driver: (update) => {
    function frame() {
      manualTime += 16; // имитация времени
      update(manualTime);
      requestAnimationFrame(frame);
    }
    requestAnimationFrame(frame);
    return () => {};
  }
});

Здесь:

  • время не зависит от реального performance.now
  • можно управлять вручную (например, через UI)

Работа с паузой

Пауза реализуется через остановку накопления времени:

let paused = false;
let pauseTime = 0;

function frame() {
  if (!paused) {
    const current = performance.now() - pauseTime;
    update(current);
  }
  requestAnimationFrame(frame);
}

При возобновлении:

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

Связь времени и easing-функций

Метки времени напрямую влияют на easing:

import { easeInOut } from "popmotion";

const progress = elapsed / duration;
const eased = easeInOut(progress);

Easing-функции:

  • принимают нормализованный прогресс
  • возвращают преобразованное значение

Таким образом:

  • время → прогресс → easing → итоговое значение

Фрейм-дроп и компенсация времени

Если кадры пропускаются (например, при нагрузке), метки времени позволяют компенсировать это:

const delta = currentTime - previousTime;

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

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

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

Popmotion использует внутренний механизм framesync, который:

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

Пример:

import sync from "framesync";

sync.update(({ timestamp }) => {
  console.log(timestamp);
});

Это обеспечивает:

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

Точность и особенности времени

Особенности работы с метками времени:

  • performance.now() более точен, чем Date.now()
  • значения — с плавающей точкой
  • возможны небольшие расхождения между кадрами

Важно учитывать:

  • нестабильный FPS
  • нагрузку на CPU/GPU
  • поведение браузера во вкладках в фоне

Практические рекомендации

  • использовать performance.now() для точных вычислений
  • избегать зависимости от фиксированного FPS
  • опираться на разницу времени (delta), а не на кадры
  • нормализовать прогресс перед применением easing
  • использовать общую временную базу для синхронных анимаций
  • учитывать паузы и возможные задержки

Внутренний цикл анимации Popmotion

Упрощённая схема:

  1. Получение текущей метки времени
  2. Вычисление elapsed
  3. Расчёт progress
  4. Применение easing
  5. Интерполяция значения
  6. Вызов onUpdate
  7. Проверка завершения

Каждый шаг зависит от корректной работы с временными метками, что делает их фундаментальной частью всей системы анимаций.