Text that morphs from one string to the next, letters gliding to their new place.
Installation
Usage
<script setup lang="ts">
import { MorphingText } from '@animate-ui/vue/primitives/texts/morphing';
</script>
<template>
<MorphingText :text="['Fast', 'Accessible', 'Animated']" loop />
</template>Letters are animated one by one, so a line can break inside a word. Prefer texts short enough not to wrap.
Animation
- Each text holds for
hold-delay, then morphs into the next. The letters both texts share, in the same order, slide to their new place onspring.selection(stiffness 300, damping 30): they never cross each other. The others fade out into a blur (6px) in 150ms, and the new ones rise in from it (0.12em, 300ms on the ease-out curve), 80ms later and 12ms apart. - Each letter takes exactly its width in the whole text, so the font's kerning holds: at rest, the text reads as if it were set in one piece.
- Emoji and accented letters stay whole.
- The cycle pauses off screen and in a hidden tab.
<AnimateUIProvider :tempo>paces the slide, the fades and the delays.- Under reduced motion, the texts keep cycling as a plain crossfade.
- Screen readers read the current text whole, not letter by letter. It is not a live region.
API Reference
MorphingText
| Prop | Type | Default |
|---|---|---|
text | string | string[] | - |
loop? | boolean | false |
hold-delay? | number | 2500 |
delay? | number | 0 |
initial? | TargetAndTransition | VariantLabels | boolean | { opacity: 0, y: '0.12em', filter: 'blur(6px)' } |
animate? | TargetAndTransition | VariantLabels | boolean | { opacity: 1, y: 0, filter: 'blur(0px)' } |
exit? | TargetAndTransition | VariantLabels | { opacity: 0, filter: 'blur(6px)' }, in 150ms |
variants? | Variants | - |
transition? | Transition | { type: 'spring', stiffness: 300, damping: 30, opacity and filter: { duration: 0.3, ease: [0.23, 1, 0.32, 1] } } |
in-view? | boolean | false |
in-view-margin? | string | '0px' |
in-view-once? | boolean | true |
...props? | Omit<HTMLMotionProps<'span'>, 'children'> | - |