text-morphText morph
文字形变Text that animates character by character when it changes, drawing attention to the new value.
文字变化时逐字符播放动画,把注意力引向新值。
Transition between similarly sized short labels while preserving reading position.
Text that animates character by character when it changes, drawing attention to the new value.
English definitions are preserved verbatim from Animation Vocabulary; translations and distinctions help you choose precisely.
text-morphText that animates character by character when it changes, drawing attention to the new value.
文字变化时逐字符播放动画,把注意力引向新值。
Purpose, frequency, trigger, timing, interruptibility, and gesture rules shape the implementation.
Transition smoothly between similarly sized short text values and direct attention to the change.
Use for low- to medium-frequency metrics, status words, and short labels, replacing body paragraphs directly.
Trigger on value change and animate only the characters or phrase that changed.
Soften and replace old glyphs as new glyphs appear on the same baseline, keeping container width predictable.
Coalesce rapid updates and settle only on the latest text.
No gesture is required; use an aria-live strategy that avoids character-by-character announcements.
Replace the full text immediately and use static highlighting or one short fade to signal change.
Current parameters update visual-state CSS, semantic HTML, the required vanilla JS runtime, and an interaction prompt.
.motion-demo.motion-demo--text-morph {
position: relative;
display: grid;
place-items: center;
width: min(100%, 34rem);
min-height: 15rem;
padding: 2rem;
overflow: hidden;
color: #18181b;
background: #fafafa;
border: 1px solid #e4e4e7;
border-radius: 12px;
isolation: isolate;
}
.motion-demo.motion-demo--text-morph *, .motion-demo.motion-demo--text-morph *::before, .motion-demo.motion-demo--text-morph *::after {
box-sizing: border-box;
}
.motion-demo.motion-demo--text-morph .motion-surface {
position: relative;
display: grid;
gap: 0.65rem;
width: min(100%, 18rem);
padding: 1.25rem;
background: #ffffff;
border: 1px solid #e4e4e7;
border-radius: 8px;
box-shadow: 0 1px 2px rgb(0 0 0 / 6%), 0 10px 28px rgb(0 0 0 / 7%);
}
.motion-demo.motion-demo--text-morph .motion-line {
display: block;
width: 100%;
height: 0.5rem;
background: #e4e4e7;
border-radius: 999px;
}
.motion-demo.motion-demo--text-morph .motion-line:last-child { width: 62%; }
.motion-demo.motion-demo--text-morph .motion-button {
position: relative;
min-height: 2.75rem;
padding: 0.75rem 1.1rem;
color: white;
font: inherit;
font-weight: 700;
background: #18181b;
border: 0;
border-radius: 7px;
cursor: pointer;
}
.motion-demo.motion-demo--text-morph .motion-button:focus-visible { outline: 3px solid rgb(37 99 235 / 32%); outline-offset: 3px; }
.motion-demo.motion-demo--text-morph .motion-action-row {
display: flex;
justify-content: center;
gap: 0.5rem;
margin-top: 0.85rem;
}
.motion-demo.motion-demo--text-morph .motion-replay-scene {
width: min(100%, 22rem);
display: grid;
place-items: center;
}
.motion-demo.motion-demo--text-morph .motion-pausable-scene {
width: min(100%, 22rem);
display: grid;
place-items: center;
}
.motion-demo.motion-demo--text-morph button.motion-surface {
color: inherit;
font: inherit;
text-align: start;
cursor: pointer;
}
.motion-demo.motion-demo--text-morph .motion-replay-button,
.motion-demo.motion-demo--text-morph .motion-undo-button,
.motion-demo.motion-demo--text-morph .motion-dismiss-button,
.motion-demo.motion-demo--text-morph .motion-pause-button {
min-height: 2.75rem;
padding: 0.65rem 0.9rem;
color: #18181b;
font: 650 0.78rem/1 ui-sans-serif, system-ui, sans-serif;
background: #ffffff;
border: 1px solid #d4d4d8;
border-radius: 7px;
cursor: pointer;
}
.motion-demo.motion-demo--text-morph .motion-replay-button:focus-visible,
.motion-demo.motion-demo--text-morph .motion-undo-button:focus-visible,
.motion-demo.motion-demo--text-morph .motion-dismiss-button:focus-visible,
.motion-demo.motion-demo--text-morph .motion-pause-button:focus-visible,
.motion-demo.motion-demo--text-morph [data-reorder-item]:focus-visible,
.motion-demo.motion-demo--text-morph [data-swipe-target]:focus-visible {
outline: 3px solid rgb(37 99 235 / 32%);
outline-offset: 3px;
}
.motion-demo.motion-demo--text-morph .motion-status {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.motion-demo.motion-demo--text-morph .motion-ripple-ink {
position: absolute;
width: 12px;
height: 12px;
pointer-events: none;
background: rgb(255 255 255 / 72%);
border-radius: 50%;
transform: translate(-50%, -50%) scale(0.95);
opacity: 0;
}
.motion-demo.motion-demo--text-morph .motion-state-stack { position: relative; width: min(100%, 18rem); height: 9rem; }
.motion-demo.motion-demo--text-morph .motion-state { position: absolute; inset: 0; display: grid; place-items: center; padding: 1rem; background: #ffffff; border: 1px solid #e4e4e7; border-radius: 8px; }
.motion-demo.motion-demo--text-morph .motion-state--to { background: #18181b; color: white; }
.motion-demo.motion-demo--text-morph .motion-scroll-track > span { color: #71717a; font: 600 0.72rem/1.2 ui-monospace, monospace; letter-spacing: 0.08em; text-transform: uppercase; }
.motion-demo.motion-demo--text-morph .motion-orbit-item { display: grid; place-items: center; width: 2.5rem; height: 2.5rem; color: white; background: #2563eb; border-radius: 50%; }
.motion-demo.motion-demo--text-morph .motion-text-stack { position: relative; width: min(100%, 18rem); min-height: 4rem; }
.motion-demo.motion-demo--text-morph svg { width: min(100%, 20rem); height: auto; }
.motion-demo.motion-demo--text-morph .motion-word { position: absolute; font-size: 2rem; font-weight: 700; animation: motion-text-morph 240ms cubic-bezier(0.23, 1, 0.32, 1) 120ms both; }
.motion-demo.motion-demo--text-morph .motion-word--from { animation-name: motion-text-morph-out; }
@keyframes motion-text-morph { from { opacity: 0; filter: blur(8px); transform: translateY(0.35em); } to { opacity: 1; filter: blur(0); transform: none; } }
@keyframes motion-text-morph-out { to { opacity: 0; filter: blur(8px); transform: translateY(-0.35em); } }
@keyframes motion-text-morph-reduced-fade {
from { opacity: 0.84; }
to { opacity: 1; }
}
.force-reduced-motion .motion-demo.motion-demo--text-morph {
animation: motion-text-morph-reduced-fade 160ms cubic-bezier(0.23, 1, 0.32, 1) both !important;
}
.force-reduced-motion .motion-demo.motion-demo--text-morph *,
.force-reduced-motion .motion-demo.motion-demo--text-morph *::before,
.force-reduced-motion .motion-demo.motion-demo--text-morph *::after {
animation: none !important;
animation-delay: 0ms !important;
animation-iteration-count: 1 !important;
transition-property: opacity, color, background-color, border-color !important;
transition-duration: 160ms !important;
transition-delay: 0ms !important;
scroll-behavior: auto !important;
}
.force-reduced-motion .motion-demo.motion-demo--text-morph .motion-button,
.force-reduced-motion .motion-demo.motion-demo--text-morph [data-reorder-item],
.force-reduced-motion .motion-demo.motion-demo--text-morph [data-swipe-target],
.force-reduced-motion .motion-demo.motion-demo--text-morph [data-spring-target] {
transform: none !important;
}
.force-reduced-motion .motion-demo.motion-demo--text-morph .motion-path { stroke-dashoffset: 0 !important; }
.force-reduced-motion .motion-demo.motion-demo--text-morph .motion-word--from { opacity: 0 !important; visibility: hidden; }
.force-reduced-motion .motion-demo.motion-demo--text-morph .motion-word:not(.motion-word--from) { opacity: 1 !important; filter: none !important; transform: none !important; }
@media (prefers-reduced-motion: reduce) {
.motion-demo.motion-demo--text-morph {
animation: motion-text-morph-reduced-fade 160ms cubic-bezier(0.23, 1, 0.32, 1) both !important;
}
.motion-demo.motion-demo--text-morph *, .motion-demo.motion-demo--text-morph *::before, .motion-demo.motion-demo--text-morph *::after {
animation: none !important;
animation-delay: 0ms !important;
animation-iteration-count: 1 !important;
transition-property: opacity, color, background-color, border-color !important;
transition-duration: 160ms !important;
transition-delay: 0ms !important;
scroll-behavior: auto !important;
}
.motion-demo.motion-demo--text-morph .motion-button,
.motion-demo.motion-demo--text-morph [data-reorder-item],
.motion-demo.motion-demo--text-morph [data-swipe-target],
.motion-demo.motion-demo--text-morph [data-spring-target] {
transform: none !important;
}
.motion-demo.motion-demo--text-morph .motion-path { stroke-dashoffset: 0 !important; }
.motion-demo.motion-demo--text-morph .motion-word--from { opacity: 0 !important; visibility: hidden; }
.motion-demo.motion-demo--text-morph .motion-word:not(.motion-word--from) { opacity: 1 !important; filter: none !important; transform: none !important; }
}Replace the full text immediately and use static highlighting or one short fade to signal change.
| Parameter | Range / options | Default | Purpose |
|---|---|---|---|
duration | 80ms–1200ms | 240ms | Controls the time required for one motion cycle. |
blur | 0px–20px | 8px | Adjusts transition blur. |
delay | 0ms–600ms | 120ms | Controls the wait before motion begins. |
ease | Soft / Snap / Calm / Linear | Soft | Controls how velocity changes over time. |