Fix spring animation velocity loss on Chrome (vsync-aligned mousemove)#3577
Closed
mattgperry wants to merge 1 commit into
Closed
Fix spring animation velocity loss on Chrome (vsync-aligned mousemove)#3577mattgperry wants to merge 1 commit into
mattgperry wants to merge 1 commit into
Conversation
Chrome fires mousemove events at vsync rate, causing spring animations in attachFollow to be interrupted after exactly 1 frame. The previous code called stopAnimation() before reading velocity, so getVelocity() computed a cross-animation finite difference (old animation vs new keyframe) - often near-zero or wrong direction. Fix: read velocity from the spring generator's sampleAt() BEFORE stopping, using calcGeneratorVelocity for a 5ms precision window. Add canSampleVelocity, sampleAt(), and elapsed to JSAnimation to support this. Fixes #3407 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Greptile SummaryFixed critical velocity loss bug in spring animations on Chrome by reading velocity before stopping animations. Chrome fires Key Changes:
Confidence Score: 5/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[startAnimation called] --> B{currentValue == targetValue?}
B -->|Yes| C[stopAnimation & return]
B -->|No| D[Get velocity]
D --> E{activeAnimation?.canSampleVelocity?}
E -->|Yes| F[Sample velocity from generator<br/>using calcGeneratorVelocity]
E -->|No| G[Use value.getVelocity<br/>cross-frame finite difference]
F --> H[stopAnimation]
G --> H
H --> I[Create new JSAnimation<br/>with preserved velocity]
style F fill:#90EE90
style G fill:#FFB6C1
style I fill:#87CEEB
Last reviewed commit: d1b51bd |
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bug
Spring animations using
followValue/springValue/useSpringbehave incorrectly in Chrome when following a pointer. The ball feels "stuck" and resists orbiting the cursor, while Firefox and Safari work correctly.Fixes #3407
Root Cause
Chrome fires
mousemoveevents at vsync rate (aligned withrequestAnimationFrame), which means a spring animation is consistently interrupted after exactly 1 frame whenever the target changes.In
attachFollow'sstartAnimation(), the old code calledstopAnimation()before readingvalue.getVelocity(). After only 1 frame,getVelocity()computes a finite difference between the previous animation's last rendered position and the new animation's first keyframe — values from different animations, possibly with different targets. This cross-animation delta is often near zero or directionally wrong, causing the spring to restart with near-zero velocity every time.Fix
Read velocity from the active animation's spring generator before stopping it, using a 5ms finite difference against the generator's formula directly (
calcGeneratorVelocity+sampleAt). This gives accurate instantaneous spring velocity even when the animation has only run for a single frame.Three new members were added to
JSAnimation:canSampleVelocity— guards that the animation is running and the generator is accessiblesampleAt(t)— samples the generator's position at timet(milliseconds)elapsed— exposescurrentTimefor use as the sampling reference pointThe
startAnimation()function infollow-value.tsnow uses these to read velocity before callingstopAnimation().Tests
Added 6 unit tests in
JSAnimation.test.tsunder "JSAnimation velocity sampling (for spring interruption fix)" covering the new API members and the velocity calculation.