useAnimationOnDidUpdate Реакция реализации ловушки - PullRequest
3 голосов
/ 01 апреля 2020

Итак, я хочу использовать requestAnimationFrame для анимации чего-либо с помощью реакционных хуков.
Я хочу что-то маленькое, поэтому react-spring / animated / react-motion - плохой выбор для меня.
Я реализовал useAnimationOnDidUpdate но работает неправильно, вот воспроизведение с деталями.
Что здесь не так: при втором нажатии множитель анимации начинается с 1, но всегда должен начинаться с 0 (простая интерполяция от 0 до 1 ).
Поэтому я пытаюсь понять, почему хук сохранил предыдущее значение, хотя я уже начал новую анимацию l oop.
Вот полный список кодов для хука:

import { useState, useEffect, useRef } from 'react';

export function useAnimationOnDidUpdate(
  easingName = 'linear',
  duration = 500,
  delay = 0,
  deps = []
) {
  const elapsed = useAnimationTimerOnDidUpdate(duration, delay, deps);
  const n = Math.min(1, elapsed / duration);

  return easing[easingName](n);
}

// https://github.com/streamich/ts-easing/blob/master/src/index.ts
const easing = {
  linear: n => n,
  elastic: n =>
    n * (33 * n * n * n * n - 106 * n * n * n + 126 * n * n - 67 * n + 15),
  inExpo: n => Math.pow(2, 10 * (n - 1)),
  inOutCubic: (t) => t <.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,
};

export function useAnimationTimerOnDidUpdate(duration = 1000, delay = 0, deps = []) {
    const [elapsed, setTime] = useState(0);
    const mountedRef = useRef(false);

    useEffect(
        () => {
            let animationFrame, timerStop, start, timerDelay;

            function onFrame() {
                const newElapsed = Date.now() - start;

                setTime(newElapsed);
                if (newElapsed >= duration) {
                    console.log('>>>> end with time', newElapsed)
                    return;
                }
                loop();
            }

            function loop() {
                animationFrame = requestAnimationFrame(onFrame);
            }

            function onStart() {
                console.log('>>>> start with time', elapsed)
                start = Date.now();
                loop();
            }

            if (mountedRef.current) {
                timerDelay = delay > 0 ? setTimeout(onStart, delay) : onStart();
            } else {
                mountedRef.current = true;
            }

            return () => {
                clearTimeout(timerStop);
                clearTimeout(timerDelay);
                cancelAnimationFrame(animationFrame);
            };
        },
        [duration, delay, ...deps]
    );

    return elapsed;
}

1 Ответ

1 голос
/ 03 апреля 2020

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

Вы можете решить эту проблему, добавив setTime(0) к вам onFrame функцию, когда ожидается, что анимация стоп.

Примерно так:

function onFrame() {
  const newElapsed = Date.now() - start;

  if (newElapsed >= duration) {
    console.log('>>>> end with time', newElapsed)
    setTime(0)
    return;
  }

  setTime(newElapsed);
  loop();
}

Я знаю, может показаться странным, что он не сбрасывается сам. Но имейте в виду, что ваша анимация использует один и тот же экземпляр хука как для уменьшения, так и для уменьшения. Поэтому эта очистка необходима.

Примечание: Я также переместил строку setTime(newElapsed), чтобы она шла после оператора if, поскольку в этом нет необходимости, если оператор if true.

ОБНОВЛЕНИЕ:

Чтобы еще больше улучшить работу, вы можете переместить setTime(0) к очистке return.

This будет означать, что ваша onFrame функция изменится на:

function onFrame() {
  const newElapsed = Date.now() - start;

  if (newElapsed >= duration) {
    console.log('>>>> end with time', newElapsed)
    setTime(0)
    return;
  }

  setTime(newElapsed);
  loop();
}

А затем обновите вашу return очистку для useAnimationTimerOnDidUpdate до:

return () => {
  clearTimeout(timerStop);
  clearTimeout(timerDelay);
  cancelAnimationFrame(animationFrame);
  setTime(0);
};

Я предполагаю, что причина, по которой ваша анимация "не работает должным образом", заключалась в том, что компонент будет иметь значение sh. Что касается моего тестирования, это обновление исправляет это.

...