Skip to content

feat(app-store): animate.rise, row artwork whose cards rise out of the surface and sink back - #14

Merged
keiver merged 1 commit into
mainfrom
feat/app-store-rise
Oct 10, 2026
Merged

keiver merged 1 commit into
mainfrom
feat/app-store-rise

Conversation

@keiver

@keiver keiver commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

animate: { rise: seconds } beside animate: { rows: seconds }, for the header, search results and In-App Event videos.

What it does

  • The rows stay put. The surface is empty; cards come up out of it and sink back a few at a time, each at the same moments of every loop, so the loop closes. The still shows every card up.
  • The look is the SVG's own: what is drawn through the mask fades in with its card, and the file's filters (feOffset, feDropShadow, feGaussianBlur) grow with the card's height, so a drop shadow lengthens as the card lifts. A shape drawn on top inside one card comes up with it.
  • A card is drawn by itself once per height and laid over the surface. Cards that draw the same share their sprites. The fade is applied and dithered in the blend, so a faint card's gradient does not band.
  • With every card up, the frame is checked against the still; art that fails gets a message naming the rule.

Fixes found on the way

  • Stills of row artwork with filters are drawn past the canvas and cropped: the shadow of a card cut by the canvas edge now matches the others. Art without filters draws byte for byte as before and keeps its cache key.
  • --set appStore.<placement>.animate.rows=20, as the README shows it, failed with Unknown --set path. It is accepted, with animate.rise.
  • renderRows writes the mask region with maskUnits="userSpaceOnUse".

Measured

  • animate.rows is unchanged: its 92 existing tests pass untouched, and TomoTV's In-App Event stills come out byte-identical.
  • A 20 s 3840x1646 header with music: 41 s on an M1 Max. Search results (3840x2560): 73 s.
  • 416 tests pass, 20 new. Coverage 95.76% statements, 85.21% branches.

README (Cards that rise), schema and CHANGELOG are updated.

animate: { rise: seconds } beside animate: { rows: seconds }. The rows stay put: cards come up
out of an empty surface and sink back a few at a time, each at the same moments of every loop,
so the loop closes. The look is the SVG's own. What is drawn through the mask fades in with its
card, and the file's filters (feOffset, feDropShadow, feGaussianBlur) grow with the card's
height, so a drop shadow lengthens as the card lifts. A card is drawn by itself once per height
and laid over the surface; cards that draw the same share their sprites, and the fade is
dithered in the blend so a faint card's gradient keeps its depth. With every card up the frame
is checked against the still, and art that fails says why.

fix(app-store): stills of row artwork with filters are drawn past the canvas and cropped, so the
shadow of a card cut by the canvas edge matches the others. Art without filters draws byte for
byte as before and keeps its cache key.

fix(cli): --set appStore.<placement>.animate.rows=20, as the README shows it, is accepted.

renderRows writes the mask region in canvas px with maskUnits="userSpaceOnUse", which a window
away from the source canvas needs when the source mask left the units out.
@keiver keiver added the version:minor 1.x.0 label Oct 10, 2026
@keiver keiver self-assigned this Oct 10, 2026
@keiver
keiver merged commit ef88f4c into main Oct 10, 2026
2 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Released as v1.7.0 and published to npm.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant