wui
组件

进入视口 In View

当内容进入视口时播放可配置的入场动画。

基础示例

Loading…
pnpm dlx wui@latest add @wui/in-view
components/ui/in-view.tsx
"use client"

import * as React from "react"
import {
  motion,
  useInView as useMotionInView,
  useReducedMotion,
  type HTMLMotionProps,
  type Transition,
  type UseInViewOptions,
  type Variants,
} from "motion/react"

const defaultVariants: Variants = {
  hidden: { opacity: 0, y: 16 },
  visible: { opacity: 1, y: 0 },
}

export interface InViewProps extends Omit<
  HTMLMotionProps<"div">,
  "children" | "variants"
> {
  /** Content animated when it enters the viewport. */
  children: React.ReactNode
  /** Hidden and visible animation states. */
  variants?: Variants
  /** Motion transition used between states. */
  transition?: Transition
  /** Intersection observer options such as `margin` and `amount`. */
  viewOptions?: UseInViewOptions
  /** Only play the entrance animation once. @default true */
  once?: boolean
  /** HTML element rendered by the component. @default "div" */
  as?: React.ElementType
}

/** Animates content when it enters the viewport. */
function InView({
  children,
  variants = defaultVariants,
  transition = { duration: 0.45, ease: "easeOut" },
  viewOptions,
  once = true,
  as = "div",
  ...props
}: InViewProps) {
  const ref = React.useRef<HTMLElement>(null)
  const reduceMotion = useReducedMotion()
  const isInView = useMotionInView(ref, { ...viewOptions, once })
  const MotionComponent = React.useMemo(() => motion.create(as), [as])

  return (
    <MotionComponent
      ref={ref}
      data-slot="in-view"
      initial={reduceMotion ? false : "hidden"}
      animate={reduceMotion || isInView ? "visible" : "hidden"}
      variants={variants}
      transition={reduceMotion ? { duration: 0 } : transition}
      {...props}
    >
      {children}
    </MotionComponent>
  )
}

export { InView }

组件作用

InView 适合延后展示长页面中的说明、数据段或媒体内容。它使用浏览器交叉观察能力判断可见性,默认只播放一次,并在减少动态效果偏好下直接显示最终状态。

组件属性

PropTypeDefaultDescription
children *ReactNodeContent animated when it enters the viewport.
variantsVariants{ hidden: { opacity: 0, y: 16 }, visible: { opacity: 1, y: 0 }, }Hidden and visible animation states.
transitionTransition{ duration: 0.45, ease: "easeOut" }Motion transition used between states.
viewOptionsUseInViewOptionsIntersection observer options such as `margin` and `amount`.
oncebooleantrueOnly play the entrance animation once.
asElementType<any, keyof IntrinsicElements>divHTML element rendered by the component.

事件

组件透传底层元素的原生事件。需要获知可见状态时,可通过自定义 variants 的 Motion 回调处理;viewOptions 支持 marginamount 等观察参数。

扩展使用

通过 as="section" 调整语义元素;设置 once={false} 可在每次进入和离开视口时重播。对于很长的列表,应只为关键内容添加入场效果。