组件
动效 Motion
基于 motion 构建的可选动画基础组件和预设库。
基础示例
I slide up and fade in when I mount.
pnpm dlx wui@latest add @wui/motion"use client"
import * as React from "react"
import { Slot } from "radix-ui"
import {
motion,
useReducedMotion,
type HTMLMotionProps,
type Transition,
type Variants,
} from "motion/react"
export type MotionPreset =
| "fade"
| "scale"
| "slide-up"
| "slide-down"
| "slide-left"
| "slide-right"
| "pop"
| "blur"
/** Enter animations, expressed as `hidden` → `visible` variants. */
export const motionPresets: Record<MotionPreset, Variants> = {
fade: { hidden: { opacity: 0 }, visible: { opacity: 1 } },
scale: {
hidden: { opacity: 0, scale: 0.95 },
visible: { opacity: 1, scale: 1 },
},
"slide-up": {
hidden: { opacity: 0, y: 12 },
visible: { opacity: 1, y: 0 },
},
"slide-down": {
hidden: { opacity: 0, y: -12 },
visible: { opacity: 1, y: 0 },
},
"slide-left": {
hidden: { opacity: 0, x: 12 },
visible: { opacity: 1, x: 0 },
},
"slide-right": {
hidden: { opacity: 0, x: -12 },
visible: { opacity: 1, x: 0 },
},
pop: {
hidden: { opacity: 0, scale: 0.8 },
visible: { opacity: 1, scale: 1 },
},
blur: {
hidden: { opacity: 0, filter: "blur(6px)" },
visible: { opacity: 1, filter: "blur(0px)" },
},
}
/** Reusable transition presets. */
export const motionTransitions = {
smooth: { duration: 0.3, ease: "easeOut" },
spring: { type: "spring", stiffness: 400, damping: 30, mass: 0.7 },
snappy: { type: "spring", stiffness: 700, damping: 35 },
} satisfies Record<string, Transition>
export type MotionTransition = keyof typeof motionTransitions
const MotionSlot = motion.create(Slot.Root)
export interface MotionProps
extends Omit<HTMLMotionProps<"div">, "variants" | "transition"> {
/** Named enter animation. @default "fade" */
preset?: MotionPreset
/** A named transition preset or a custom motion Transition. @default "smooth" */
transition?: MotionTransition | Transition
/** Delay before animating, in seconds. */
delay?: number
/** Animate when scrolled into view instead of on mount. @default false */
inView?: boolean
/** Only animate the first time it enters the viewport. @default true */
once?: boolean
/** Merge motion onto the single child element instead of rendering a div. */
asChild?: boolean
}
/**
* A small opt-in wrapper that animates its children with a named preset.
* Respects `prefers-reduced-motion` (renders statically when set). Use
* `asChild` to animate an existing element (e.g. a Button) without an extra
* wrapper node.
*/
function Motion({
preset = "fade",
transition = "smooth",
delay,
inView = false,
once = true,
asChild = false,
...props
}: MotionProps) {
const reduceMotion = useReducedMotion()
const Comp = (asChild ? MotionSlot : motion.div) as React.ElementType
if (reduceMotion) {
return <Comp {...props} />
}
const resolvedTransition =
typeof transition === "string" ? motionTransitions[transition] : transition
const activation = inView
? { initial: "hidden", whileInView: "visible", viewport: { once } }
: { initial: "hidden", animate: "visible" }
return (
<Comp
data-slot="motion"
variants={motionPresets[preset]}
transition={{ ...resolvedTransition, delay }}
{...activation}
{...props}
/>
)
}
export { Motion }
组件作用
Motion 是 wui 的可选动画层。你可以包裹任意内容,为其添加自然的入场动画;也可以使用
asChild 为现有组件(如 Button)添加动画,而不增加额外的包装节点。组件内置一组命名
presets 和 transitions,以确保应用中的动画风格一致。当用户偏好减少动态效果时,
它会自动禁用动画。
由于 Motion 是独立的 registry 条目,不使用动画的组件不会引入相关依赖;只有需要动画时
才会拉取 motion。
组件属性
| Prop | Type | Default | Description |
|---|---|---|---|
| preset | "fade" | "scale" | "slide-up" | "slide-down" | "slide-left" | "slide-right" | "pop" | "blur" | fade | Named enter animation. |
| transition | Transition | "smooth" | "spring" | "snappy" | smooth | A named transition preset or a custom motion Transition. |
| delay | number | — | Delay before animating, in seconds. |
| inView | boolean | false | Animate when scrolled into view instead of on mount. |
| once | boolean | true | Only animate the first time it enters the viewport. |
| asChild | boolean | false | Merge motion onto the single child element instead of rendering a div. |
它还接受 motion.div 的所有属性,例如 whileHover、whileTap 和 style。
事件
| Prop | Type | Default | Description |
|---|---|---|---|
| onAnimationStart | (definition) => void | — | 动画开始时触发。 |
| onAnimationComplete | (definition) => void | — | 动画结束时触发。 |
扩展使用
动画预设
Loading…
滚动进入视口时播放(inView)
Loading…
为现有组件添加动画(asChild)
Loading…
减少动态效果
Motion 会读取 prefers-reduced-motion,当用户要求减少动态效果时静态渲染子元素,
无需额外处理。