Search code examples
javascriptreact-nativerequestanimationframe

RequestAnimationFrame pause and resume


I'm using requestAnimationFrame to update every 1 sec a timer.

const [secondsPassed, setSecondsPassed] = useState(0)


const t0Ref = useRef(Date.now())
const requestRef = useRef()
const targetValueRef = useRef(0)

const updateSecondsPassed = useCallback(() => {
 const time = Date.now() - t0Ref.current
 const nextValue = time * 0.001

 if (nextValue >= sessionTime) {
  console.log('Reset to 0')
  setSecondsPassed(0)
  return
 }

 const targetValue = targetValueRef.current

 if (nextValue >= targetValue) {
  console.log(`Update ${targetValue - 1} -> ${nextValue | 0}`)
  setSecondsPassed(targetValue)
  targetValueRef.current = targetValue + 1
 }

 requestRef.current = requestAnimationFrame(updateSecondsPassed)
}, [sessionTime])

useEffect(() => {
 if (playbackState === 'initial' || playbackState === 'stopped') {
  setSecondsPassed(0)
  targetValueRef.current = 0
 }

 if (playbackState === 'playing') {
  t0Ref.current = Date.now()
  requestRef.current = requestAnimationFrame(updateSecondsPassed)
 }

 if (playbackState === 'paused') {
  console.log(secondsPassed)
 }

 if (playbackState === 'resumed') {
  // Code here...
 }

 return () => cancelAnimationFrame(requestRef.current)
}, [playbackState])

The problem is I tried everything on the 'resumed' playbackState block and it waits for the number of seconds already passed before starting to count again.


Solution

  • Solved it by resetting the t0Ref to Date.now() minus the already passed seconds in milliseconds:

       if (playbackState === 'resumed') {
          t0Ref.current = Date.now() - secondsPassed * 1000
          requestRef.current = requestAnimationFrame(updateSecondsPassed)
       }