wui
组件

渐进模糊 Progressive Blur

利用多层重叠梯度遮罩与 Backdrop Filter 实现边缘平滑衰减虚化、防止硬截断的视觉层次组件。

第三方依赖 · motion

基础用法

在动态列表或长文本卡片底部叠加多层渐进模糊,使超出部分自然虚化并提供滚动心理暗示:

Loading…

安装与引入

通过 CLI 自动添加组件,或手动复制源码至项目中:

pnpm dlx @wui-design/cli@latest add @wui/progressive-blur
安装基础依赖与动效库
pnpm add motion class-variance-authority clsx tailwind-merge
复制组件源码到 components/ui/progressive-blur.tsx
components/ui/progressive-blur.tsx
"use client"

import * as React from "react"
import { motion, type HTMLMotionProps } from "motion/react"

import { cn } from "@/lib/utils"

const gradientAngles = {
  top: 0,
  right: 90,
  bottom: 180,
  left: 270,
} as const

export interface ProgressiveBlurProps extends HTMLMotionProps<"div"> {
  /** Edge that receives the strongest blur. @default "bottom" */
  direction?: keyof typeof gradientAngles
  /** Number of overlapping mask bands. Values below 2 are clamped. @default 8 */
  blurLayers?: number
  /** Blur added by each successive layer in pixels. @default 0.25 */
  blurIntensity?: number
}

function ProgressiveBlur({
  direction = "bottom",
  blurLayers = 8,
  blurIntensity = 0.25,
  className,
  ...props
}: ProgressiveBlurProps) {
  const layers = Math.max(Math.round(blurLayers), 2)
  const segmentSize = 1 / (layers + 1)
  const angle = gradientAngles[direction]

  return (
    <motion.div
      aria-hidden="true"
      data-slot="progressive-blur"
      className={cn("pointer-events-none relative", className)}
      {...props}
    >
      {Array.from({ length: layers }, (_, index) => {
        const stops = [index, index + 1, index + 2, index + 3].map(
          (position, stopIndex) =>
            `rgba(0, 0, 0, ${stopIndex === 1 || stopIndex === 2 ? 1 : 0}) ${position * segmentSize * 100}%`
        )
        const maskImage = `linear-gradient(${angle}deg, ${stops.join(", ")})`
        const blur = Math.max(0, index * blurIntensity)

        return (
          <div
            key={index}
            data-slot="progressive-blur-layer"
            className="absolute inset-0 rounded-[inherit]"
            style={{
              maskImage,
              WebkitMaskImage: maskImage,
              backdropFilter: `blur(${blur}px)`,
              WebkitBackdropFilter: `blur(${blur}px)`,
            }}
          />
        )
      })}
    </motion.div>
  )
}

export { ProgressiveBlur, gradientAngles }

属性 Props

属性类型默认值说明
direction"top" | "right" | "bottom" | "left""bottom"虚化衰减的主边缘方向(即模糊度最高的一侧)。
blurLayersnumber8用于平滑过度的遮罩层分段数量(最小自动限制为 2)。
blurIntensitynumber0.25每个分层逐级递增的模糊增量系数(像素)。数值越大,模糊总强度越高。
classNamestring—应用于外层定位容器的 CSS 类名(通常配置 absolute inset-x-0 bottom-0 等定位)。
...propsHTMLMotionProps<"div">—其余属性(如 style、initial / animate 等 Motion 动画属性)作用于外层容器,例如可对整个模糊层做淡入淡出。

事件 Events

该组件为纯展示型分层视觉遮罩层,自身不派发专用业务交互事件,默认标记 pointer-events: none 且不阻挡底层鼠标点击。

使用场景与设计规范

ProgressiveBlur 适用于截断列表提示、吸顶导航栏底部、图片渐隐遮罩:

  • 告别生硬的纯色遮罩:传统的 bg-gradient-to-t from-background 容易遮盖住文字的色彩,而渐进模糊(Backdrop Blur)则能保留底层色相同时让文本轮廓柔和化。
  • 层数建议:通常 6 ~ 8 层可以在极佳的平滑度与 GPU 显存消耗之间取得完美平衡。

场景示例

上下方多方向虚化对比

在图片上叠加顶部或底部的渐进模糊,为标题与悬浮导航提供可读的底衬,而不必使用生硬的遮罩色块:

Loading…

无障碍与交互 Accessibility

  • 点击穿透:容器默认携带 pointer-events-none,用户隔着模糊区域点击下方的链接或按钮依然能够精准响应。
  • 无障碍树隐藏:默认设置 aria-hidden="true",屏幕阅读器不受任何视觉滤镜干扰。