组件
文本变形 Text Morph
在文字变化时让共享字符流畅地移动到新位置。
基础示例
Loading…
pnpm dlx wui@latest add @wui/text-morph"use client"
import * as React from "react"
import { LayoutGroup, motion, useReducedMotion } from "motion/react"
import { cn } from "@/lib/utils"
export interface TextMorphProps extends React.ComponentProps<"p"> {
/** Text whose shared characters morph between positions. */
children: string
/** HTML element rendered by the component. @default "p" */
as?: React.ElementType
}
function characterKeys(text: string) {
const seen = new Map<string, number>()
return Array.from(text).map((character) => {
const count = seen.get(character) ?? 0
seen.set(character, count + 1)
return `${character}-${count}`
})
}
/** Morphs shared characters into their new positions when the text changes. */
function TextMorph({
children,
as = "p",
className,
...props
}: TextMorphProps) {
const reduceMotion = useReducedMotion()
const Component = as
const keys = React.useMemo(() => characterKeys(children), [children])
if (reduceMotion) {
return (
<Component data-slot="text-morph" className={className} {...props}>
{children}
</Component>
)
}
return (
<LayoutGroup>
<Component
aria-label={children}
data-slot="text-morph"
className={cn("inline-flex", className)}
{...props}
>
{Array.from(children).map((character, index) => (
<motion.span
aria-hidden="true"
data-slot="text-morph-character"
className="inline-block whitespace-pre"
layout
layoutId={keys[index]}
transition={{ type: "spring", stiffness: 420, damping: 35 }}
key={keys[index]}
>
{character === " " ? "\u00a0" : character}
</motion.span>
))}
</Component>
</LayoutGroup>
)
}
export { TextMorph }
组件作用
TextMorph 为新旧文本中相同的字符建立布局关联,适合按钮标签、短状态和简短标题。字符越相近,形变越自然;完全不同的长文本更适合 TextEffect。
组件属性
| Prop | Type | Default | Description |
|---|---|---|---|
| children * | string | — | Text whose shared characters morph between positions. |
| as | ElementType<any, keyof IntrinsicElements> | p | HTML element rendered by the component. |
事件
组件没有自有回调,更新 children 即会触发布局动画,并透传底层元素的原生事件。
扩展使用
通过 as="span" 放入按钮或句子中。视觉字符对辅助技术隐藏,完整的新文本会作为元素标签读取;减少动态效果时直接替换文字。