rooks icon indicating copy to clipboard operation
rooks copied to clipboard

Elapsed time in `useRaf` resets on every tick

Open iamriot-dev opened this issue 3 years ago • 4 comments

Describe the bug The callback passed to useRaf takes in one parameter timeElapsed, this is supposed to represent the number of milliseconds passed since the hook started running. However, currently timeElapsed resets on every animation frame.

To Reproduce Steps to reproduce the behavior:

function Component() {
  const [ms, setMs] = useState(0);

  useRaf((timeElapsed) => {
    setMs(timeElapsed)
  }, true);

  return (
    <p>Elapsed time: {ms}</p>
  );
}

Expected behavior timeElapsed should increase over time.

iamriot-dev avatar Jun 27 '22 10:06 iamriot-dev

I thought this is how the useRaf hook is supposed to work. Isn't it supposed to represent the time delta, instead of the time passed since the beginning?

qqpann avatar Jun 27 '22 11:06 qqpann

I might have misinterpreted it then, the documentation does not really specify what it is supposed to be. What are the use cases for having the time delta between frames? I am currently trying to use this hook to run an image sequence animation, taking into account the time elapsed to calculate which frame to show.

E.g.

useRaf(timeElapsed => {
  let newFrameIndex = Math.floor(timeElapsed / 50);
  if (newFrameIndex <= lastFrameIndex) {
    setActiveFrameIndex(newFrameIndex);
  } else {
    setActiveFrameIndex(lastFrameIndex);
    setAnimationDidEnd(true);
  }
}, !animationDidEnd);

Perhaps the hook could instead pass an object that includes both the time elapsed from start and the time delta between frames?

iamriot-dev avatar Jun 27 '22 11:06 iamriot-dev

@iamriot-dev So speaking from the top of my head, the hook is working correctly based on my original intended usage, but we can also expose total time elapsed from start time if that helps your scenario.

imbhargav5 avatar Jun 29 '22 22:06 imbhargav5

For my use case, I made a custom hook so it's easier to replace setInterval

I feed intervalsPassed to the callback to handle edge case like "switch to another tab for a while then switch back"

import { useState } from "react"
import { useRaf } from "rooks"

type Millisecond = number

export function useRafInterval(
  callback: (intervalsPassed: number ) => void,
  time: Millisecond,
  isActive: boolean = true
) {
  const [cache, setCache] = useState(0)

  useRaf(timeElapsed => {
    const temp = cache + timeElapsed
    if (temp >= time) {
      // console.log('temp time', temp)
      // console.log('temp time in second', temp / 1000)
      callback(Math.floor(temp / time))
      setCache(0)
    } else {
      setCache(temp)
    }
  }, isActive)
}

Ding-Fan avatar Jul 27 '22 09:07 Ding-Fan

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Mar 20 '23 09:03 stale[bot]