Add hardware acceleration support for useSpring via WAAPI#3547
Add hardware acceleration support for useSpring via WAAPI#3547mattgperry wants to merge 2 commits intomainfrom
Conversation
When useSpring creates a spring animation and the browser supports linear() CSS easing, the MotionValue now carries an .accelerate config through the useSpring → useTransform → bindToMotionValue pipeline. - Add AccelerateConfig interface and .accelerate property to MotionValue - In attachFollow, set .accelerate with spring factory/options when the spring can be hardware-accelerated (supportsLinearEasing + damping != 0) - In useTransform, propagate .accelerate from input to output when using range-based transform with clamp enabled, replacing times/keyframes/ease - In bindToMotionValue, when .accelerate is present on an acceleratable value (opacity, transform, filter, clipPath), listen for animationStart and create a WAAPI animation with spring easing via linear() CSS function https://claude.ai/code/session_01L9oKDA2qPgTAzt5MYPLUbU
PR Review: Hardware Acceleration Support for useSpring via WAAPIThis is an impressive feature that enables hardware-accelerated spring animations by leveraging WAAPI with CSS ✅ Strengths
🔍 Potential Issues & Suggestions1. Error handling in VisualElement.ts (Lines 578, 645)The empty catch blocks silently swallow errors: try { activeAcceleration.cancel() } catch (e) {}Issue: If Suggestion: Add minimal logging or comment explaining why errors are expected: try {
activeAcceleration.cancel()
} catch (e) {
// Animation may already be cancelled/finished - safe to ignore
}2. Type safety in follow-value.ts:123const { type, onUpdate, onComplete, onPlay, onRepeat, onStop, ...springOptions } = options as anyIssue: Using Suggestion: Define a proper type or use a type assertion with a more specific type: type SpringTransitionOptions = Omit<ValueAnimationTransition, 'type' | 'onUpdate' | 'onComplete' | 'onPlay' | 'onRepeat' | 'onStop'>
const { type, onUpdate, onComplete, onPlay, onRepeat, onStop, ...springOptions } = options as SpringTransitionOptions3. Non-null assertion in VisualElement.ts:595factory.applyToOptions!(transition)Issue: The Suggestion: Add a runtime check: if (!factory.applyToOptions) {
// Fallback or early return
return
}
factory.applyToOptions(transition)4. Potential race condition in VisualElement.ts:582-583const accel = value.accelerate
if (!accel) returnIssue: If Suggestion: This is likely fine given JavaScript's single-threaded nature, but consider documenting the expected lifecycle of 5. useTransform keyframes type assertion (Line 237)keyframes: outputRange as AnyResolvedKeyframe[],Issue: The Suggestion: While the runtime checks ensure this is safe, consider adding a comment explaining why this assertion is valid, or add a runtime type guard if 6. Performance consideration: animationStart eventThe Question: Has performance been tested with components animating multiple properties simultaneously? Consider profiling with a stress test. 7. Missing validation for keyframes/times array lengthsIn VisualElement.ts:601-604, the code assumes const keyframeOptions: PropertyIndexedKeyframes = {
[key]: keyframes as string[],
}
if (times) keyframeOptions.offset = timesIssue: If Suggestion: Add validation: if (times && times.length !== keyframes.length) {
// Log warning or skip acceleration
return
}📝 Code Style (per CLAUDE.md)✅ Good use of optional chaining (e.g., 🧪 Testing Recommendations
🔒 SecurityNo security concerns identified. The code doesn't accept user input directly and operates on internal animation state. 🎯 PerformanceOverall looks good, but consider:
📦 Bundle SizeThe changes add ~150 lines to core packages. Given the performance benefits, this seems reasonable. Consider:
✨ Overall AssessmentStrong approval with minor refinements suggested. This is a well-architected feature that maintains backward compatibility while enabling significant performance improvements. The test coverage is excellent, and the implementation follows the repository's conventions. Priority fixes:
Nice-to-haves:
Great work! 🚀 |
Replace manual WAAPI construction (applyToOptions, mapEasingToNativeEasing, element.animate) with a single NativeAnimation instantiation that handles spring-to-linear() easing conversion, keyframe commitment, and cleanup internally. https://claude.ai/code/session_01L9oKDA2qPgTAzt5MYPLUbU
PR Review: Hardware Acceleration Support for useSpring via WAAPII've reviewed this PR and overall it's a well-architected feature that brings hardware acceleration to spring animations. Here's my detailed feedback: ✅ Strengths
🔍 Issues & Concerns1. Type Safety Issue (VisualElement.ts:585)keyframes: accel.keyframes as string[],Problem: Impact: Runtime type mismatch could cause WAAPI to fail silently or throw. Fix: Either:
|
2. Potential Race Condition (follow-value.ts:117-131)value.attach((v, set) => {
// ... animation setup
}, stopAnimation)
// accelerate config set AFTER attach
value.accelerate = {
factory: spring,
// ...
}Problem: The Impact: First animation might not be hardware-accelerated. Fix: Set 3. Missing Validation (VisualElement.ts:579-580)const accel = value.accelerate
if (!accel) returnProblem: No validation that required properties exist on Impact: Potential runtime errors. Fix: Add runtime validation or use TypeScript non-null assertions more carefully. |
4. Performance: Unnecessary Animation Creation (VisualElement.ts:576-590)removeAnimationStart = value.on("animationStart", () => {
activeAcceleration?.stop()
// create new NativeAnimation every time
})Problem: Creates a new WAAPI animation on every Impact: Minor performance overhead, potential jank on rapid value changes. Suggestion: Consider caching the NativeAnimation and only recreating when 5. Code Style Violation (follow-value.ts:123)const { type, onUpdate, onComplete, onPlay, onRepeat, onStop, ...springOptions } = options as anyProblem:
Fix: Just use value.accelerate = {
factory: spring,
options, // Already the right type
times: [0, 1],
keyframes: [0, 1],
ease: "linear",
} |
6. Inconsistent Behavior: clamp Default (use-transform.ts:228-233)Question: Why is The comment says "clamp enabled (default)" but:
Suggestion: Document this decision more clearly, or consider propagating for function transforms too. 🎯 Minor Issues
|
🛡️ Security & Performance
📋 RecommendationsCritical (should fix before merge):
Important (should fix): Nice to have: SummaryThis is solid work that adds an important optimization. The main concerns are around type safety and a potential race condition. With those fixes, this would be ready to merge. Great job on the test coverage and architectural design! 🚀 Reviewed using repository guidelines from CLAUDE.md |
When useSpring creates a spring animation and the browser supports
linear() CSS easing, the MotionValue now carries an .accelerate config
through the useSpring → useTransform → bindToMotionValue pipeline.
spring can be hardware-accelerated (supportsLinearEasing + damping != 0)
range-based transform with clamp enabled, replacing times/keyframes/ease
value (opacity, transform, filter, clipPath), listen for animationStart
and create a WAAPI animation with spring easing via linear() CSS function
https://claude.ai/code/session_01L9oKDA2qPgTAzt5MYPLUbU