Skip to content

fix(core): phase-sync useTrail children on loop iterations#2435

Merged
joshuaellis merged 2 commits into
nextfrom
worktree-1063
May 21, 2026
Merged

fix(core): phase-sync useTrail children on loop iterations#2435
joshuaellis merged 2 commits into
nextfrom
worktree-1063

Conversation

@joshuaellis
Copy link
Copy Markdown
Member

Closes #1063.

Problem

useTrail chains each child via to: parent.springs, so every parent change re-enters the child's _startAnimatedValue.reset(), zeroing its progress each frame. With loop: true, the head snap-resets to from each cycle but children only chase fluidly — they trap in a narrowing band near the mid-range and never sweep the full from→to distance. Deeper children trap further from the target.

Fix

Add an internal Controller.onLoopReset subscription that fires synchronously inside flushUpdate just before createLoopUpdate recurses. useTrail subscribes every non-head child directly to the head controller — the only controller whose flushUpdate reaches the loop tail (children's to is fluid, so their updates never finish). Chaining via immediate parents would cascade only one level; subscribing to the head keeps the whole trail in phase.

On loop restart, each child snaps back to from via ctrl.start({ reset: true }), then resumes chasing the parent through the fluid chain. The fluid-chain contract (load-bearing for the goo-blobs mouse-follow demo) is preserved — the existing 'has each spring follow the spring before it' test still passes.

Out of scope

loop triggers when the head finishes, so deeper springs can still be cut off before reaching to under short-duration configs. That's a separate API design choice worth a follow-up.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 21, 2026

🦋 Changeset detected

Latest commit: 8ad79e6

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@react-spring/core Patch
@react-spring/animated Patch
@react-spring/mock-raf Patch
@react-spring/parallax Patch
@react-spring/rafz Patch
@react-spring/shared Patch
@react-spring/types Patch
@react-spring/konva Patch
@react-spring/native Patch
@react-spring/three Patch
@react-spring/web Patch
@react-spring/zdog Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown

vercel Bot commented May 21, 2026

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

Project Deployment Actions Updated (UTC)
react-spring Ready Ready Preview May 21, 2026 7:47pm

Request Review

Closes #1063.

useTrail chains each child via `to: parent.springs`, so every parent
change re-enters the child's `_start` → `AnimatedValue.reset()`, zeroing
its progress each frame. With `loop: true`, the head snap-resets to
`from` each cycle but children only chase fluidly — they trap in a
narrowing band near the mid-range and never sweep the full distance.

Add an internal `Controller.onLoopReset` subscription that fires
synchronously inside `flushUpdate` just before `createLoopUpdate`
recurses. `useTrail` subscribes every non-head child directly to the
head (the only controller whose `flushUpdate` reaches the loop tail —
chaining via immediate parents would cascade only one level) and snaps
each child back to `from` in phase. The fluid-chain contract relied on
by goo-blobs is preserved.
@joshuaellis joshuaellis merged commit 46f97db into next May 21, 2026
16 checks passed
@joshuaellis joshuaellis deleted the worktree-1063 branch May 21, 2026 19:48
This was referenced May 21, 2026
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.

[bug]: useTrail does not work correctly (spring values are weaker the more trails you have)

1 participant