PlAnimateSplit
A line of text arriving one part at a time. The other effects tell themselves off across their children; a line of text has none, so this one makes them.
import { PlAnimateSplit } from 'plass-ui';
<PlAnimateSplit effect="slide" stagger={60}>
One design language, two libraries
</PlAnimateSplit>;import 'package:plass_ui/plass_ui.dart';
const PlAnimateSplit(text: 'One design language, two libraries');Props
| Prop | Type | Default | Description |
|---|---|---|---|
| children * | string | — | The line. A string, and it has to be: the component cuts it up, and there is nothing to cut inside a <strong> |
| by | 'word' | 'character' | 'word' | What it is cut into. character is not safe in every script: Arabic stops joining and an emoji is cut into its pieces |
| effect | 'fade' | 'grow' | 'slide' | 'zoom' | 'rotate' | 'blink' | 'reveal' | 'fade' | Which of the entrances each part plays |
| durationshared | number | 400 | How long one run takes, in milliseconds. A number, never a CSS string |
| delayshared | number | 0 | How long before it starts, in milliseconds |
| easingshared | string | the house curve | The easing curve, written the way CSS writes it |
| repeatshared | number | 'infinite' | 1 | How many times it runs. 'infinite' rather than Infinity, because that word is what reaches CSS |
| alternateshared | boolean | false | Runs every other pass backwards, so a repeat returns instead of jumping |
| pausedshared | boolean | false | Holds the animation where it is |
| triggershared | 'mount' | 'visible' | 'hover' | 'manual' | 'mount' | What starts it: mount as soon as it is on the page, visible when it is scrolled into view, hover while the pointer or focus is on it, manual only when play says so |
| playshared | boolean | — | Runs it when trigger is manual. Each false → true starts it over |
| onceshared | boolean | true | With trigger="visible", whether it runs only the first time. Off, it runs again every time the element comes back into view |
| thresholdshared | number | 0.2 | With trigger="visible", how much of the element has to be on screen before it counts as visible, from 0 to 1 |
| timelineshared | 'auto' | 'view' | 'auto' | What advances the animation: the clock, or the reader's scroll position. view ignores duration, delay, repeat and trigger, and runs against range instead |
| rangeshared | string | 'entry 0% cover 45%' | As CSS writes an animation-range. Only read when timeline is view |
| staggershared | number | 0 | Milliseconds added to each child's delay. 0 plays the box itself; anything else moves the effect onto the children and takes it off the box |
| durationStepshared | number | 0 | Milliseconds added to each child's duration. Negative is allowed; floored at 0 |
| reverseshared | boolean | false | Runs the set from the last child to the first. Only the order turns round; each child still plays forwards |
| render | ReactElement | (props, state) => ReactElement | — | Renders something other than a <span> (<h2 />) |
| Prop | Type | Default | Description |
|---|---|---|---|
| text * | String | — | The line. A string, and it has to be: the component cuts it up, and there is nothing to cut inside a <strong> |
| by | PlAnimateSplitBy | PlAnimateSplitBy.word | What it is cut into. character is not safe in every script: Arabic stops joining and an emoji is cut into its pieces |
| style · textAlign | TextStyle? · TextAlign? | — | The text style the line is drawn in, and how the parts align once they wrap |
| from · distance · fade | PlassSide · double · bool | bottom · 12 · true | The entrance, spelled as a side, a distance and a fade. React names a CSS keyframe instead, because there an effect is a named thing and here every effect is built out of widgets |
| stagger · reverse | Duration · bool | 40ms · false | How long after one part the next one starts, and whether it starts from the end of the line |
| durationshared | Duration | Duration(milliseconds: 400) | How long one run takes, in milliseconds. A number, never a CSS string |
| delayshared | Duration | Duration.zero | How long before it starts, in milliseconds |
| curveshared | Curve? | the house curve | The easing curve, written the way CSS writes it |
| repeatshared | int? | 1 | How many times it runs. null is what never stops: there is no 'infinite' to write, and -1 would be a sentinel a caller has to look up |
| alternateshared | bool | false | Runs every other pass backwards, so a repeat returns instead of jumping |
| pausedshared | bool | false | Holds the animation where it is |
| triggershared | PlassAnimateTrigger | PlassAnimateTrigger.mount | What starts it: mount as soon as it is on the page, visible when it is scrolled into view, hover while the pointer or focus is on it, manual only when play says so |
| playshared | bool | false | Runs it when trigger is manual. Each false → true starts it over |
| onceshared | bool | true | With trigger="visible", whether it runs only the first time. Off, it runs again every time the element comes back into view |
| thresholdshared | double | 0.2 | With trigger="visible", how much of the element has to be on screen before it counts as visible, from 0 to 1 |
by="character" is not safe in every script
The one thing to know before using it.
A character part breaks the shaping between letters. Arabic stops joining, Devanagari conjuncts come apart, and an emoji built out of several code points is cut into its pieces, so a line that was one word becomes a row of unrelated glyphs.
word has none of those problems, is the default, and is what a headline wants anyway: a word arriving is something a reader can follow, and a letter arriving is decoration.
Gaps and parts
Whitespace is left as whitespace and never given an entrance of its own, animating the space between two words is nothing arriving, and it does not take a step of the stagger with it either. The second word starts one step after the first, not two.
Each part is inline-block, because a transform does not apply to a non-replaced inline element: without it a slide would fade and never move.
Writing the entrance
effect picks one of the seven keyframes, and stagger, durationStep and reverse mean exactly what they mean on a PlAnimateFade around a list of <li>s. This component is the splitting and nothing more.
The entrance is a side, a distance and a fade, exactly as PlAnimateAppear spells it, which is the widget that already tells one entrance off across a set of children.
The React build names a CSS keyframe instead, and that difference is not an inconsistency: over there an effect is a named thing the stylesheet knows about, and here every effect is built out of widgets. A split takes the parameters the widget beside it takes.
Accessibility
- A screen reader is told the line, once. The parts are hidden from the accessibility tree and the whole line sits beside them, which is what stops a split headline being read out one word, or one letter, at a time. That is the defect this pattern is known for everywhere it appears without it.
- Text selection and copying still give you the line, gaps included.
- Where a reader has asked for less motion nothing plays, and the line is simply there.