AnimationDirectiveDOMAdded in v15.7

Falling Letters

Click a line of text and its letters let go: each one drops with gravity, drifts and tumbles a little, and lands on whatever already lies under it, so the letters pile into a heap on the floor of a container. Click again, or wait, and every letter springs back to its place.

Installation

Import the class, and @use the entry point in your global styles. Nothing warns you when the @use is missing.

import { Component } from '@angular/core';
import { WrFallingLetters } from 'ngwr/animations/falling-letters';

@Component({ imports: [WrFallingLetters] })
export class MyComponent {}

Basic

Put wrFallingLetters on the element that holds the text. The floor and the walls are the content box of the host's parent, measured when the fall starts, so give the line a container with room under it. No physics engine: a letter's path is gravity plus a drift and a spin drawn from a fixed sequence by its index, so the same text falls the same way every time. Letters meet on the floor, a letter that lands fast hops once or twice, and the heap lies still for rest ms, 4000 unset, before springing back.

<section class="arena">
  <p wrFallingLetters>Click this line and watch it fall.</p>
</section>

Click this line and watch it fall.

A descendant as the target

A selector as the directive's value points it at one descendant of the host instead, for a paragraph a library component draws. The target is looked up once after the first render by its public BEM class; a selector that matches nothing turns the effect off.

<wr-result-404 wrFallingLetters=".wr-result__description" />

Floor

floor is a selector for the element whose content box is the floor and the walls, found with closest from the host; unset, the host's parent. On a display: contents host that is the box around it. On a wr-* host write it as a binding, since a static attribute that is not one of the component's inputs fails check:attrs.

<main>
  <p wrFallingLetters [floor]="'main'" rest="2000">Lands on the floor of <code>main</code>.</p>
</main>

Lands on the floor of the arena, not of its own parent.

Accessibility

PartBehaviour
Screen readers The authored nodes are moved into an invisible wrapper that holds exactly their space, still the real text for a screen reader and the clipboard. The pieces are aria-hidden, out of the selection and under no pointer, so a button a letter falls across stays clickable.
Reduced motion Under prefers-reduced-motion: reduce a click does nothing, and a preference that flips mid-fall puts the text back at once. The demos on this page follow the same setting.
Layout The line never moves: the pieces are positioned from the target's own box and move by transform alone, so nothing under them reflows. When the last letter is back, the same node objects go back where they were, so a binding inside the text keeps working.
Server Nothing happens on the server or when the target has no box. The click listener is added in the browser, after the first render.

API

NameDescriptionTypeDefault
wrFallingLettersA selector for one descendant of the host whose letters fall instead of the host's own, written as the directive's value: wrFallingLetters=".wr-result__description". Resolved once after the first render with querySelector; a selector that matches nothing turns the effect off. Unset or empty, the host.string | nullnull
floorA selector for the element whose content box is the floor and the walls, found with closest from the host. Unset, the host's parent element: on a display: contents host that is the box around it. On a wr-* host write it as a binding ([floor]="'main'"), since a static attribute that is not one of the component's inputs fails check:attrs.string | nullnull
restHow long the heap lies still before the letters spring back on their own, in ms.number4000

See also