PlAnimateShake
A refusal. The one effect in the set that is a response rather than an entrance, so it starts held still, and replay is how a caller says "again".
import { PlAnimateShake } from 'plass-ui';
<PlAnimateShake replay={attempts}>
<PlTextField label="Password" type="password" error={error} invalid />
</PlAnimateShake>;import 'package:plass_ui/plass_ui.dart';
PlAnimateShake(
replay: attempts,
child: PlTextField(label: const Text('Password'), error: error, invalid: true),
);Props
| Prop | Type | Default | Description |
|---|---|---|---|
| replay | unknown | — | Plays the shake again whenever this value changes, and never on the first render. A refusal can happen twice, and a boolean cannot say "again" |
| distance | number | string | 6 | How far it travels either side of where it started |
| 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 |
| render | ReactElement | (props, state) => ReactElement | — | Renders something other than a <div> |
| Prop | Type | Default | Description |
|---|---|---|---|
| replay | Object? | — | Plays the shake again whenever this value changes, and never on the first render. A refusal can happen twice, and a boolean cannot say "again" |
| distance | double | 6 | How far it travels either side of where it started |
| 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 |
| child * | Widget | — | What is refused |
What the shared animation props mean is on any of the other transitions.
replay is why it exists
A refusal can happen twice, and play (being a boolean) cannot say "again". Replaying with it means toggling off and on: two renders for one event, and a piece of state whose only job is to be flipped back.
A value that has changed is the closest React has to an event, and the count of failed attempts a form already keeps is exactly that value.
const [attempts, setAttempts] = useState(0);
<PlAnimateShake replay={attempts}>…</PlAnimateShake>;It never plays on the first render. A shake that shook itself on mount would be answering an event that has not happened.
A reaction to an event
Every other effect here answers "how does this content arrive" and starts on mount. This one answers something the reader did, so it starts held still (trigger defaults to manual), and plays only when it is told to.
It is also not in PlassAnimation, the union mode and stagger are built on, for PlAnimateFloat's reason: that union is the set of ways content can arrive, and a response is not an arrival.
The rest position
Three shudders either side of home and back to nothing.
That matters more here than anywhere else in the group, because this is the one effect a caller will run over content that is still being typed into. A field left a few pixels off its label would be a worse defect than the error it was reporting.
Examples
A locked control
<PlAnimateShake replay={refusals}>
<PlButton disabled>Delete workspace</PlButton>
</PlAnimateShake>A shorter, wider shudder
<PlAnimateShake replay={attempts} distance={10} duration={300}>Accessibility
- A reader who asked for less motion sees none of it, and that is exactly why the words matter more than the shake. Whatever the refusal is saying has to be said in text as well (an
erroron the field, a message in a live region), and the shake is emphasis, never the message. - Shaking a field does not tell a screen reader anything. Pair it with the field's own
errorandinvalid, which do. - It is decoration around the content, not a wrapper that changes what the content is: what is inside keeps its own role, its own focus and its own name.