Repository navigation
feat(registry): gooey split block with metaball goo and directional motion blur - #5123
Conversation
…otion blur A dot splits into a three-dot menu through a gooey neck, spins into a swirl mark under shutter motion blur, breaks back into three dots and merges into one. Timing, spacing and eases are measured off a reference recording frame by frame. The motion-blur snippet gains an opt-in `sharp: 0` option that hides the crisp frame-time copy while an element moves, leaving only the shutter average. The default is unchanged; the inlined copies carry the same text.
…isibility Hiding through the element's own visibility fought timelines that write it (autoAlpha): a target faded in after attach stayed hidden at rest. A marker attribute and one !important rule now do the hiding, the default path writes nothing, and a declarative sharp other than 0 or 1 is refused like any other bad option.
…ttached Copies froze the target's computed visibility at attach, so a target entering with autoAlpha never smeared (and vanished entirely under sharp 0). The group now carries the target's current visibility each frame, read with the sharp 0 marker lifted, and a copy keeps a descendant's visibility only where it differs from its parent's.
The smear group set an explicit visibility on every moving frame, and copies used to freeze an explicit visible, so an ancestor clip hiding after the last update could not hide them: copies from a 0-4 s sub-composition stayed on screen at 16.4 s. The group now sets a visibility only where the element's differs from its parent's. Also regenerates skills-manifest.json for the motion-blur skill doc.
Edit accuracy: accurate 2055 (base branch 2055), smooth 1515 of thoseThe gate passes. Quarantined, measured but not gated (0) |
jrusso1020
left a comment
There was a problem hiding this comment.
Approving at 4ffc0331. Most of my reading went to the shared motion-blur snippet, since every existing user of motion-blur and shutter-slam gets those changes. The block itself is a self-contained composition.
Shared snippet:
sharp: 0can't leave an element hidden. The marker comes off at the start of everyapplyShutterpass, before the frame's transform is read, and goes back on only when the element moved. An element at rest is always sharp.discard()only runs when attach throws, which is before any frame has set the marker, so a torn-down target can't keep it either. The option stays opt-in: with the defaultsharp: 1,installHideRulenever runs and the marker is never set.- Visibility now follows the element instead of being frozen at attach. The group sits as the element's previous sibling, so leaving
visibilityempty when the element matches its parent lets an ancestor that hides later, like an ended clip, hide the smear too. That fixes the stale-copies report as a class: no copy and no group ever pins a visibility of its own, so every ancestor hiding path gets it, not just sub-composition clips. snapshot()'s temporaryvisible !importantis undone exactly. It goes back to the inline value and its priority, inside afinally. An inline!importantalso beats the marker's stylesheet!important, so asharp: 0target hidden by the marker is read as shown while its styles are copied.- The shadow-root case listed under "not exercised" would need a target that is a direct child of a shadow root, where
getComputedStyle(parentNode)would throw. Compositions run inside the player's iframe, and only the iframe container sits in the player's shadow root, so a composition element doesn't hit that case today. - Copies:
gooey-split,motion-blurandshutter-slameach carry one snippet between the sentinels, and the copy-equality test holds them equal. Thedocs/cataloganddocs/public/catalogcopies still show the old snippet. That's expected:check-catalog-source-prkeeps generated catalog output out of source PRs, andcatalog-publishregenerates it after merge.
Block: it loads GSAP and CustomEase from jsdelivr like the other blocks, has no Math.random or clock reads, and its only setTimeout is the snippet's own registry poll. registry-item.json matches the existing block shape.
Local: motionBlurShutter.browser.test.ts passed 60/60 on each of two runs. The other registry test files need @hyperframes/core built, which I didn't do. CI covers them and is green at this head: 95 pass and 1 skipped, including every required check.
Non-blocking: visibility is read with getComputedStyle(parentNode) once per target per frame, on top of the per-sample transform reads. That's negligible at the block's three targets, but worth keeping in mind if someone marks dozens of elements.
— Rames
What
A new catalog block,
gooey-split: a dot splits into a three-dot menu through a gooey (metaball) neck, spins into a swirl mark under directional motion blur, breaks back into three dots, and merges into one. 1920x1080, 60 fps, 4.7 s. Timing, spacing and eases were measured frame by frame off a reference recording.How
sharp: 0hides the crisp frame-time copy while an element moves, leaving only the shutter average (a soft blur end to end). It hides through a marker attribute and one!importantrule, never the element's own visibility, soautoAlphatimelines keep working. The default (sharp: 1) writes nothing new to the element. A declarativesharpother than 0 or 1 is refused like any other bad option.autoAlpha(hidden when blurred) never smeared. The smear group now carries the element's current visibility each frame, like its opacity, and a copy keeps a descendant's visibility only where the descendant sets its own. This is one behaviour change for existing users: such elements now smear.hidden. The smear group now sets a visibility only where the element's differs from its parent's, and copies no longer freeze one, so an ancestor that hides later hides the smear too.motion-blurandshutter-slamcarry the same text, as the copy-equality test requires.Verification
packages/cli/src/registry/motionBlurShutter.browser.test.ts: 60 passed, exit 0, three runs in a row. Ten mutations of the changed snippet lines each turn their own test red.hyperframes linton the block: 0 errors, 1 warning (file size, from inlining the snippet, as inshutter-slam).autoAlphainside a clip, then moves: before the fix, mid-move,sharp: 1showed no smear andsharp: 0showed nothing; after, both smear and still frames are unchanged.hyperframes snapshot --at 16.4and a render frame at 16.4 s: stale copies with the snippet on main, none with this branch.skills-manifest.jsonregenerated withgen:skills-manifest; its--checkpasses.The frames below are the render: split, mid-spin blur, blob into blade, the mark, break, merge. Then the Studio preview against the render at the same instants, and the MP4.
Review
Independent adversarial review, one report per head. Final round at this head (4ffc033): no BLOCKERs, no MAJORs, no MINORs.
sharp: 0kept anautoAlphatarget hidden; 4 MINOR (default path dropped an inline!important, default path untested,sharpvalues unchecked, doc)finally; restored priority untestedtry/finally; testgooey-split.mp4