Components with a move hook (componentWillMove, onComponentWillMove, used by
inferno-animation) prepare keyed lists before their layout changes. Each section checks a previous glitch.
Animated cards are orange, plain cards are grey. The status line compares what happened with what should happen.
Only card 3 has a move hook. Deferring its DOM move used to place card 4 incorrectly. All DOM moves now follow the same reconciliation order. The list should read 0 1 4 3 2.
All cards are animated. Moving D to the top and adding X must measure the original A B C D layout: A, B and C should slide two rows without jumping. Insertion-only and removal-only updates should also animate the retained cards, even though their relative order is unchanged.
The callback must use the type and props of the component that owns the hook.
Reversing either list should animate to C B A without throwing a TypeError.
onComponentWillMovecomponentWillMove
Both lists use AnimatedMoveComponent. On the left it renders an element, on the right it renders
a function component that renders the same element. Both lists should animate and receive the same
number of preparation callbacks.
<li><Card>
Sections 5–15 use AnimatedMoveComponent and AnimatedAllComponent with 1.5 s moves,
enters and leaves. Each section says what should happen and what used to go wrong. The status line measures the
first frame of the animation or its end result, so wait for the previous animation to finish before pressing a button
again. A red status means that the glitch reproduced.
A move measures boxes in viewport pixels and applies the offset as a CSS translate in the item's own
coordinate space, so it converts the offset into that space. Cards should start exactly where they were and slide
to their new places. Before the conversion the cards started at the wrong distance and jumped at the start of the
move: 40 % short inside a transform: scale(0.6) ancestor, 40 % short with
scale: 0.6 on the cards themselves (the individual transform properties apply before
transform), and twice too far in an SVG whose viewBox scales its content by 2.
The cards of the last list are children of a shadow root, whose host is scaled: a list in a shadow root used to
throw when it moved, which stopped every animation of that update. CSS zoom and 3D transforms are
not taken into account.
transform: scale(0.6)scale: 0.6<g> items, viewBox at half sizescale(0.6)
Card C starts with display: none. The button moves E to the top and shows C in the same update.
C should simply appear in its place (between B and D) while the others slide. A hidden element has an empty box
at the viewport origin: C used to be treated as moved from the top-left corner of the window and flew in from
there.
transform
Card B pulses with a CSS @keyframes animation that sets transform, and card D has
popped in with one that has finished and keeps its last keyframe (animation-fill-mode: forwards).
Moving E to the top should slide B and D down one row like A and C: they move with the translate
property. A keyframe animation overrides an inline transform, so B and D used to jump to their new
places while their neighbours slid.
The buttons move E to the top and remove cards 0.6 s later, halfway through the moves. The cards that leave
should stay where they are on screen and leave from there. The leave used to finish the move first, so E jumped
to the top of the list and collapsed there. Removing the whole list or replacing every card did the same to all
cards. With the first button E slides out to the right while it fades and collapses: its leave animates
transform, which keeping E in place must not stop, so the position is kept in
translate. With the other buttons the cards fade and collapse in place.
The button adds X and removes it 0.6 s later, before its enter animation has finished. X should shrink and fade
out from its current size and opacity over the 1.5 s leave. The enter transitions that the leave interrupts
fire transitioncancel, which the leave used to count as its own transitions ending, so X was removed
after a few frames. When X is removed in the next frame, before its enter has started, nothing of it has been
visible and it should be removed at once: it used to show at full opacity and fade out.
The groups and their cards have move hooks. The buttons reverse the groups and the cards inside them in one update. Everything should start where it was: the groups slide past each other and the cards slide within them. Each card's offset contains its group's movement, which the group's own transform adds again: the cards used to start far outside their groups and fly in. When the first group is removed in the same update, the cards are measured after the groups have moved up into its place: they used to start that much too high. On the right the groups have no box of their own. G1 moves two rows down and its card c two rows up, so c should not move at all: it used to start two rows higher and slide back.
The cards have transition: background-color 1s. The button moves E to the top and highlights B in the
same update. B is displaced by one row without being moved in the DOM. The readme documents that elements running
an author transition are excluded from moves, so B jumps down while A, C and D slide. Check whether that is
acceptable: selecting an item and re-sorting the list at once is a common pattern. (A card that is moved in the DOM
does not start the transition, so it slides normally.)
Each added box is rendered with style="width: 160px; height: 56px". It should appear and keep that
size. The enter animation writes its own inline width and height, and used to remove
them when it ended, together with the ones the application rendered. The box snapped to the stylesheet size
(full width, 36 px) at the end of the enter, and Inferno does not restore the style until the prop changes.
A box that is removed should keep its size while it fades out: the leave used to remove the inline size as
well, so the box snapped to the full width when its leave started. The inline height takes precedence over the
animation classes, so these boxes do not grow or collapse.
The logo has globalAnimationKey. Move in one update removes it on the left and shows it on the
right in the same task: the logo should fly from left to right (the intended global animation). Remove
and, more than a second later, Show on the right should be a plain enter, because the source expires after
one second. Within that second the logo should still fly, which allows for a page that loads before it mounts.
The expiry used to never start, so the logo still flew from where it was removed, however long ago, and unused
sources stayed in memory.
The tile moves from the left pane to the right one with globalAnimationKey. On the right it is rendered
with style="transform: rotate(-12deg)". It should fly over and end up tilted. The global enter writes
its own transform and transform-origin, and used to clear both when it activated, together
with the ones the application rendered, so the tile flew in untilted and stayed that way.
Inserts, removes and moves cards at the chosen interval, sometimes twice in one task. Watch for cards that jump, flicker, overlap or leave gaps. Stop and check waits for the animations to finish and compares the DOM with the expected order, and reports cards left with inline styles or animation classes.