Skip to content

fix(motion): read lazy preset keyframes after the measure step - #347

Draft
eliranhe wants to merge 1 commit into
masterfrom
fix/motion-lazy-keyframes
Draft

eliranhe wants to merge 1 commit into
masterfrom
fix/motion-lazy-keyframes

Conversation

@eliranhe

Copy link
Copy Markdown
Contributor

Description

Bug fix (motion): presets that compute their keyframes from measured values get keyframes from before the measurement. Cross loops sit still, and the scroll range of BgZoom and BgFake3D ignores the component's height.

Some presets return keyframes (and BgZoom/BgFake3D also endOffsetAdd) as getters. They read values that the preset's dom.measure() callback sets in the next fastdom frame. getWebAnimation() already waits for that: it calls setKeyframes() and sets the ranges inside fastdom.mutate().

Since #281, getEffectsData() calls normalizeKeyframes(effect.keyframes) right away, which runs the getter before the measure. A getter returns a new array on every read, so the keyframes === effect.keyframes check always fails, and { ...effect, keyframes } then copies every getter as the value it has at that moment. The mutate step gets those early values:

preset (WAAPI path) before after
Cross keyframe offsets 0, 0, 0, 1, 1 (the element starts outside its parent and never moves) 0, 0.64, 0.64, 1, 1
BgZoom (in) range end calc(100svh + 0px) calc(100svh + 800px)
BgFake3D parallax range end calc(100svh + 0px) calc(100svh + 800px)

These numbers come from the real presets run through getWebAnimation() in jsdom, with stubbed layout (an 800px tall component for the background presets) and fastdom queued the way a browser frame runs it.

Fix: getEffectsData() passes the preset's object through untouched. Keyframe names are normalized where they're used: inside the fastdom.mutate() in webAnimations.ts, and in getCSSAnimation(). The CSS path calls the presets' style(), which doesn't measure, so its output is unchanged.

Related Issue

Found in the Wix viewer while moving sites from interact v1 to v2: page-load Cross loops that ran under v1 never play under v2. In the investigation that found this, patching the same change into the bundle made the Cross loops on two affected sites run again, with offsets of 0.408 and 0.502 instead of 0.

Checklist

  • I have read the Contributing Guide
  • I have added/updated tests for my changes (if applicable)
  • I have updated documentation/rules/skills (if applicable): not applicable, no API change

Tests

Two new specs in packages/motion/test/motion.spec.ts › getWebAnimation(). Each one registers a preset whose getter depends on a value its dom.measure() sets, and queues fastdom so the measure runs after getWebAnimation() returns:

  • should read lazy keyframes after the preset measured the target: setKeyframes() gets offset 0.4. Without the fix, it gets 0.
  • should read lazy scrub ranges after the preset measured the target: the range end is cover calc(0% + 200px). Without the fix, it's cover calc(0% + 0px).

The existing kebab-case normalization specs still pass. yarn build, yarn test, yarn lint and yarn format:check pass.

🤖 Generated with Claude Code

getEffectsData() normalized effect.keyframes as soon as it built the
effect data, so presets whose keyframes (and ranges) are getters over
values set in their dom.measure() callback were read before the measure.
The getter returns a new array on each read, so the result was always
spread into a copy, which froze every getter at its pre-measure value.
Cross loops came out with zero offsets, and BgZoom/BgFake3D scroll
ranges ignored the component height.

Pass the preset's object through untouched and normalize keyframe names
where they are consumed: inside the fastdom.mutate() that sets the WAAPI
keyframes, and in getCSSAnimation().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

1 participant