-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add sequence callback support for animation timelines #3526
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
This implements #2205 - callbacks can now be injected at specific points in an animation sequence. Callbacks support both forward (onEnter) and backward (onLeave) firing for scrubbing support. Usage: ```js animate([ [element, { x: 100 }, { duration: 1 }], [{ onEnter: () => console.log("midpoint!") }, { at: 0.5 }], [element, { y: 200 }, { duration: 1 }], ]) ``` Implementation has minimal overhead: - SequenceCallbackAnimation is lightweight (no DOM ops, just time tracking) - Callbacks are sorted and stored separately from animations - Only fires callbacks when time crosses thresholds https://claude.ai/code/session_01Es5grCjnfwALQxsWvrrpzF
Code ReviewI've reviewed PR #3526 which adds sequence callback support for animation timelines. Overall, this is a well-implemented feature with strong test coverage. Here's my detailed feedback: ✅ Strengths1. Excellent Architecture
2. Comprehensive Test Coverage
3. Good Documentation
|
- Rename onEnter/onLeave to forward/backward for clarity - Remove dedicated SequenceCallbackAnimation class - Use animateSingleValue(0, 1) with onUpdate to track time crossings - Much simpler implementation with same functionality https://claude.ai/code/session_01Es5grCjnfwALQxsWvrrpzF
PR Review: Add sequence callback support for animation timelinesI've thoroughly reviewed this PR. Overall, this is a well-implemented feature with solid test coverage and clear design decisions. Here's my detailed feedback: ✅ Strengths1. Clean API Design
2. Excellent Test Coverage
3. Minimal Performance Overhead
4. Architecture
🔍 Issues FoundCritical: Incorrect Callback Timing LogicLocation: The current implementation recalculates for (const callback of callbacks) {
const prevTime = prevProgress * totalDuration // ❌ WRONG: recalculated each iteration
if (prevTime < callback.time && currentTime >= callback.time) {
callback.forward?.()
} else if (prevTime >= callback.time && currentTime < callback.time) {
callback.backward?.()
}
}Problem: When multiple callbacks exist at different times, Fix: Move return (progress: number) => {
const currentTime = progress * totalDuration
const prevTime = prevProgress * totalDuration // ✅ Calculate once
for (const callback of callbacks) {
if (prevTime < callback.time && currentTime >= callback.time) {
callback.forward?.()
} else if (prevTime >= callback.time && currentTime < callback.time) {
callback.backward?.()
}
}
prevProgress = progress
}This affects both files (lines 29-38 in
|
The WAAPI path should stay minimal and not pull in animateSingleValue. https://claude.ai/code/session_01Es5grCjnfwALQxsWvrrpzF
Just destructure animationDefinitions directly from createAnimationsFromSequence instead of wrapping it. https://claude.ai/code/session_01Es5grCjnfwALQxsWvrrpzF
PR Review: Add sequence callback support for animation timelinesSummaryThis PR adds callback support to animation sequences, allowing developers to execute code at specific timeline points. The implementation is clean and well-tested with comprehensive coverage. ✅ Code Quality & Best PracticesStrengths:
Minor suggestions:
🐛 Potential Issues1. Edge case: Multiple callbacks at the same time 2. Callback execution during seeks ( 3. Memory leak potential 4. WAAPI implementation (
⚡ Performance ConsiderationsExcellent:
Suggestion: // Early exit when we've passed all callbacks in forward direction
if (currentTime >= callbacks[callbacks.length - 1].time &&
prevTime >= callbacks[callbacks.length - 1].time) {
prevProgress = progress
return
}🔒 Security ConcernsNo security issues identified. Callbacks are user-provided functions executed in the user's context, which is expected behavior. 🧪 Test CoverageExcellent coverage with tests for:
Missing test cases:
📝 DocumentationThe PR description is excellent. Consider adding:
Code-Specific Commentssequence.ts:59-66 - Consider adding a comment explaining why linear ease is used: // Use linear ease to ensure onUpdate receives uniform progress values
// for accurate time threshold detectioncreate.ts:420 - Nice that callbacks are sorted! Consider adding a comment explaining why: // Sort callbacks by time for deterministic execution order
// and potential future optimization (early exit)ConclusionThis is a solid implementation that follows the codebase's high standards. The feature is well-designed, properly tested, and adds valuable functionality. The main suggestions are around edge case documentation and minor optimizations. Recommendation: Approve with minor suggestions ✅ The identified issues are low-severity and mostly around documentation/edge cases rather than fundamental problems. Nice work! |
Code Review: Sequence Callback SupportThank you for this PR! The implementation of callback support for animation sequences is well-designed and thoughtfully executed. Strengths1. Clean Architecture
2. Comprehensive Test Coverage
3. Smart Timing Logic
Potential Issues1. Critical: Callback Firing on Repeated Time Sets (HIGH PRIORITY)Location: packages/framer-motion/src/animation/animate/sequence.ts:31-37 Problem: The implementation does not explicitly track which callbacks have already fired. If progress is set externally via scrubbing, the same callback could fire multiple times during forward playback if the progress crosses the threshold multiple times. Recommendation: Consider tracking which callbacks have fired in each direction using a Set. 2. WAAPI Implementation Missing Callback Support (HIGH PRIORITY)Location: packages/framer-motion/src/animation/animators/waapi/animate-sequence.ts The WAAPI version only extracts animationDefinitions but completely ignores callbacks. This creates an inconsistent API where callbacks work in some contexts but silently fail in others. Recommendation: Either add callback support to WAAPI, log a warning when callbacks are present but ignored, or document this limitation clearly. 3. Type Safety Issue in isCallbackSegmentLocation: packages/framer-motion/src/animation/sequence/create.ts:465-477 The heuristic could incorrectly identify animation segments as callbacks if someone passes forward: 100 trying to animate a forward property. The check for duration/ease helps but is not foolproof. Recommendation: Make callbacks more explicit with a unique property, class constructor, or Symbol key for type discrimination. 4. Performance: Linear Search on Every UpdateLocation: packages/framer-motion/src/animation/animate/sequence.ts:28 On every animation frame, this loops through ALL callbacks even if none are near the current time. For sequences with many callbacks this could impact performance. Recommendation: Implement binary search or maintain a pointer to the next callback to only check callbacks near the current position. 5. Edge Case: Callbacks at Time 0If a callback is positioned at time 0, it might fire immediately when the animation starts. This might be unexpected behavior. Recommendation: Add a test case and clarify documentation on whether time-0 callbacks fire on animation start. Code Style and Best PracticesMinor Issues:
Test Coverage AssessmentExcellent coverage of timing resolution, but missing:
Recommendations SummaryMust Fix before merge:
Should Fix: Nice to Have: Overall AssessmentThis is a high-quality PR with solid implementation and thorough testing. The main concern is the missing WAAPI support and the type discrimination heuristic. With those addressed, this feature will be a valuable addition to the animation API. Score: 8/10 - Would be 9.5/10 after addressing the WAAPI issue and type safety concern. Let me know if you would like me to provide code snippets for any of the suggested fixes! |
Return ResolvedAnimationDefinitions directly instead of a wrapper object. Callbacks are passed via an optional out-parameter that only animateSequence uses. WAAPI path and tests are unaffected. https://claude.ai/code/session_01Es5grCjnfwALQxsWvrrpzF
Code Review SummaryThank you for this well-structured PR! The implementation of sequence callbacks is clean and follows good practices. Below is my detailed feedback: ✅ StrengthsCode Quality
Implementation Details
🔍 Issues & Concerns1. CRITICAL: WAAPI Implementation Missing Callback SupportThe WAAPI version of // waapi/animate-sequence.ts - Line 12
createAnimationsFromSequence(definition, options).forEach(
({ keyframes, transition }, element: Element) => {
animations.push(...animateElements(element, keyframes, transition))
}
)
// ❌ Missing: No callbacks array, no callback animation addedImpact: Callbacks will silently not work when using WAAPI animations, leading to confusing behavior differences between animation backends. Recommendation: Update the WAAPI implementation to match the main implementation, or document why callbacks are not supported in WAAPI mode. 2. Edge Case: Empty Sequence with Only Callbacks// packages/framer-motion/src/animation/animate/sequence.ts:67-71
const firstTransition = animationDefinitions.values().next().value?.transition
const totalDuration = firstTransition
? (Object.values(firstTransition)[0] as any)?.duration ?? 0
: 0If a sequence contains only callbacks and no animations,
Test case that would expose this: const sequence = [
[{ forward: () => console.log('1') }, { at: 0 }],
[{ forward: () => console.log('2') }, { at: 1 }],
]
// Expected: callbacks fire at 0s and 1s
// Actual: both fire immediately at 0sRecommendation: Either:
3. Type Safety: Unsafe
|
Summary
This PR adds support for callbacks in animation sequences, allowing developers to execute code at specific points during a sequence timeline. Callbacks can be positioned at absolute times, relative offsets, or referenced by labels, and support both forward playback (onEnter) and backward scrubbing (onLeave).
Key Changes
New
SequenceCallbackAnimationclass (motion-dom): A lightweight animation implementation that fires callbacks when the timeline crosses specific time points. It handles both forward playback and backward scrubbing scenarios.Callback segment support: Extended
AnimationSequencetype to support callback segments as[{ onEnter?, onLeave? }, { at? }]tuples, allowing callbacks to be interspersed with animations.Enhanced
createAnimationsFromSequence:CreateAnimationsResultobject containinganimationDefinitions,callbacks, andtotalDurationatpositioning logic as animations (absolute, relative+/-,<for previous, or label references)Integration with animation sequences: Updated both
animateSequencefunctions (inframer-motionandwaapi) to instantiate and includeSequenceCallbackAnimationin the animation group when callbacks are present.Comprehensive test coverage: Added 10+ tests covering callback extraction, timing resolution, multiple callbacks, label-based positioning, and interaction with animation timing.
Implementation Details
SequenceCallbackAnimationuses simple time comparison logic with minimal overheadonLeavecallbacks, enabling interactive timeline scrubbing scenarioshttps://claude.ai/code/session_01Es5grCjnfwALQxsWvrrpzF