Add layoutAnchor prop for relative projection#3647
Conversation
When a parent and child are both centered (e.g., flexbox) and the parent
expands via layout animation, the child drifts toward the top-left before
settling at center. This happens because relative projection measures from
the parent's top-left corner (parent.min), causing interpolation drift when
parent/child animation progress diverges.
layoutAnchor={{ x: 0.5, y: 0.5 }} anchors to center, eliminating drift.
layoutAnchor={false} disables relative projection entirely.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Greptile SummaryAdds a
Confidence Score: 3/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["resolveTargetDelta()"] --> B{"targetDelta or relativeTarget?"}
B -->|Neither| C{"layoutAnchor !== false?"}
C -->|Yes| D{"relativeParent && parent.layout?"}
C -->|No / false| E["removeRelativeTarget()"]
D -->|Yes| F["createRelativeTarget(anchor)"]
D -->|No| E
B -->|relativeTarget exists| G["calcRelativeBox(target, relativeTarget, parent.target, anchor)"]
B -->|targetDelta exists| H["applyBoxDelta(target, targetDelta)"]
H --> I{"attemptToResolveRelativeTarget?"}
I -->|Yes| J["createRelativeTarget(anchor)<br/><b>⚠ Missing layoutAnchor!==false gate</b>"]
I -->|No| K["Continue"]
style J fill:#fff3cd,stroke:#ffc107
style C fill:#d4edda,stroke:#28a745
|
This test has been fixed for flakiness 5 times and still fails intermittently. The exact same #3141 scenario (rapid key alternation in mode="wait") is already covered by two unit tests in AnimatePresence.test.tsx ("Does not get stuck when state changes cause rapid key alternation in mode='wait'" and "Shows latest child after rapid key switches in mode='wait'"). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…arget
The attemptToResolveRelativeTarget code path also calls
createRelativeTarget but was missing the layoutAnchor === false check,
meaning layoutAnchor={false} wouldn't fully disable relative projection
when re-resolving targets mid-animation.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Summary
Fixes #3028
layoutAnchorprop to control the anchor point for relative layout projectionlayoutAnchor={{ x: 0.5, y: 0.5 }}anchors to center, preventing child drift during parent layout animations in centered layouts (flexbox, grid)layoutAnchor={false}disables relative projection entirelyanchor=0) is unchanged — existing code produces identical resultsWhen a parent and child are both centered and the parent expands via layout animation, relative projection measures the child's offset from
parent.min. If parent/child animation progress diverges (different durations, spring easing), this causes visible drift. Anchoring to center makes the relative offset constant for centered children, eliminating the drift regardless of animation timing.Changes
delta-calc.ts: Added optionalanchorparam tocalcRelativeAxis,calcRelativeAxisPosition,calcRelativeBox,calcRelativePosition. UsesmixNumber(already imported) with a fast path whenanchor=0create-projection-node.ts: Passes anchor fromoptions.layoutAnchorthrough to allcalcRelativePosition/calcRelativeBoxcalls; gates relative target creation onlayoutAnchor !== falsetypes.ts: AddedlayoutAnchortoProjectionNodeOptionsandMotionNodeLayoutOptionsuse-visual-element.ts: PasseslayoutAnchorfrom React props to projection node optionsTest plan
delta-calc.test.ts— anchor=0 backward compat, anchor=0.5 centering, roundtrip consistency, axis-level behavior🤖 Generated with Claude Code