Skip to content

feat: pause animations in place with paused and report the frozen values with onPaused - #65

Merged
janicduplessis merged 4 commits into
mainfrom
@janic/pause-current-values
Oct 15, 2026
Merged

janicduplessis merged 4 commits into
mainfrom
@janic/pause-current-values

Conversation

@janicduplessis

@janicduplessis janicduplessis commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Closes #64.

There was no way to stop an animation where it is and continue it later. transition: { type: 'none' } cancels and snaps to the target, so a marquee that has to freeze on touch or blur had to estimate its offset from elapsed time and remount with a new initialAnimate to resume, which remounts (and reloads) its children.

This adds two props:

  • paused?: boolean: freezes the view's running animations, loops included, where they are; false resumes them from the same point. Nothing remounts.
  • onPaused?: ({ values }) => void: fires each time paused takes effect, with the on-screen values of the keys set in animate (colors as rgba() strings).
<EaseView
  initialAnimate={{ translateX: 0 }}
  animate={{ translateX: -300 }}
  transition={{ type: 'timing', duration: 3000, easing: 'linear', loop: 'repeat' }}
  paused={touching}
  onPaused={({ values }) => console.log(values.translateX)}
/>

Behavior worth weighing (also in the README):

  • iOS pauses descendants too. It stops the view layer's Core Animation clock, which child layers share, so animations inside the view (nested EaseViews, UIKit animations) pause with it. Android and web pause only the view's own animations. After a resume that clock runs behind CACurrentMediaTime(), so a descendant that computes its own beginTime from CACurrentMediaTime() starts late.
  • Web fixes that affect every user. The loop effect depended on the animate/initialAnimate object identities, so any re-render restarted a loop from its start; it now restarts only when the keyframes change (changing a loop's animate still restarts it, paused or not). The inline transition is now set in a layout effect, before the browser can compute the render's style change, so a transition always uses the current config.
  • Changes while paused start from the frozen values and play on resume; a delay counts from the resume. transition: { type: 'none' } and removing a property still apply immediately.
  • onTransitionEnd: toggling paused is a prop update, so like any re-render today it reports in-flight non-looping animations as interrupted, and their completion after the resume isn't reported.
  • Backgrounding on iOS: Core Animation drops animations while the app is in the background, so a loop (paused or not) stops for good today. That's pre-existing and fixed separately in fix(ios): replay loop animations when the app becomes active again #67; a paused non-looping animation is still lost the same way a running one is.

Per platform:

  • iOS: Core Animation pause/resume (QA1673). Explicit beginTimes for delayed and looping animations now use layer time, so animations started after a pause don't begin late.
  • Android: Animator.pause()/resume(). SpringAnimation can't pause, so a paused spring is cancelled and restarted from its last value and velocity, which determine the rest of its motion.
  • Web: getAnimations() with pause()/play(), which covers both CSS transitions and loop keyframes.

getCurrentValues() from #64 is left for a follow-up.

Test Plan

New example screen Loop → Pause / Resume: a marquee with Pause/Resume, the onPaused readout, a mount id and an image load counter, plus delayed timing, spring and enter-while-paused boxes.

iOS

ios-pause-cycles.mp4

Android (emulator, three pause/resume cycles)

ease-pause-android.mp4

Web (Chrome, three pause/resume cycles)

ease-pause-web.mp4

Three pause/resume cycles per platform on the 40 pt/s marquee. Positions were tracked on every frame of the original recordings, using their real timestamps. Resume jump = where the motion after resume extrapolates back to, minus the frozen position; steady motion is 0.67 pt/frame.

Platform Pauses Resume jump
iOS (iPhone 16 Pro sim, iOS 18.6) 1.1 s, 2.6 s, 5.6 s +0.24, −0.29, +0.27 pt
Android (local emulator, screenrecord) 0.45 s, 2.0 s, 5.1 s first resume step −1.14, −1.21, −0.73 dp: up to one extra frame, see the README note
Web (Chrome, every requestAnimationFrame) 0.5 s, 2.0 s, 5.0 s first resume step −0.67, −0.63, −0.61 pt (one frame)

There's no fixed offset, and nothing grows with the pause length. On iOS the pause frame steps back about 0.3 pt (Core Animation draws slightly ahead of CACurrentMediaTime()), and the resume frame makes it up.

  • iOS: onPaused matched the measured tile edges within 0.16 pt, and nothing remounted (the mount id and image load count didn't change). A 300 ms delayed timing created while paused starts 0.32 s after resume. Timing and spring boxes paused mid-move continue at the same speed. A view mounted while paused waits at its initial values and enters on resume.
  • Android: onPaused matched the screen within 0.08 dp. The paused timing and spring boxes finish at their target.
  • Web: onPaused matches the computed transform, enter animations still fade, and a view mounted while paused enters on resume.

…ues with onPaused

paused freezes a view's running animations, loops included, and resumes
them from the same point. onPaused reports the on-screen values of the
animate keys when the pause takes effect.

iOS stops the layer clock (speed 0 + timeOffset), so explicit
beginTimes are now written in layer time. Android pauses animators and
restarts springs from their last value and velocity. Web pauses the
element's CSS transitions and animations, and restarts a loop only when
its keyframes change instead of on every render.

Closes #64
@vercel

vercel Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
react-native-ease Ready Ready Preview Oct 15, 2026 12:41pm UTC

Request Review

@janicduplessis
janicduplessis marked this pull request as ready for review October 15, 2026 16:24
@janicduplessis
janicduplessis merged commit 36608e0 into main Oct 15, 2026
9 checks passed
@janicduplessis
janicduplessis deleted the @janic/pause-current-values branch October 15, 2026 16:24

This branch was successfully deployed

1 active deployment
Preview — 673f2aeb Deployed Oct 15, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pause/resume animations in place (paused prop), onPaused event, and reading current animated values

1 participant