wui
组件

动态图标 Animated Icon

图标内部路径会随交互运动的 Lucide 动态图标集合。

基础示例

这些不是对整个 SVG 做统一缩放或旋转。每个图标都有独立的逐路径动画,例如铃铛摆动、复制层分离、菜单线条重排和齿轮转动。

Loading…

安装完整集合:

pnpm dlx wui@latest add @wui/animated-icon-collection

完整集合包含 AnimateIcons 的 248 个图标和 ItsHover 去重后独有的 207 个图标,共 455 个;56 个同名重复图标只保留 AnimateIcons 版本。安装命令会同时加入 @animateicons/react、本地 ItsHover 图标源码和统一适配组件。

components/ui/animated-icon.tsx
"use client"

import * as React from "react"

export interface AnimatedIconHandle {
  startAnimation: () => void
  stopAnimation: () => void
}

export interface AnimatedIconGlyphProps extends Omit<
  React.HTMLAttributes<HTMLDivElement>,
  | "color"
  | "onAnimationEnd"
  | "onAnimationIteration"
  | "onAnimationStart"
  | "onDrag"
  | "onDragEnd"
  | "onDragStart"
> {
  size?: number
  duration?: number
  isAnimated?: boolean
  color?: string
}

export type AnimatedIconGlyph = React.ForwardRefExoticComponent<
  AnimatedIconGlyphProps & React.RefAttributes<AnimatedIconHandle>
>

export interface AnimatedIconProps extends AnimatedIconGlyphProps {
  /** Path-animated icon imported from `@animateicons/react/lucide`. */
  icon: AnimatedIconGlyph
  /** Accessible name. Omit it when the icon is purely decorative. */
  label?: string
  /** Imperative playback handle exposed by the animated glyph. */
  ref?: React.Ref<AnimatedIconHandle>
}

/**
 * A common accessible adapter for path-animated icons from AnimateIcons.
 * Every glyph keeps its own semantic motion and imperative playback handle.
 */
function AnimatedIcon({
  icon: Glyph,
  label,
  ref,
  ...props
}: AnimatedIconProps) {
  return (
    <Glyph
      ref={ref}
      data-slot="animated-icon"
      aria-hidden={label ? undefined : true}
      aria-label={label}
      role={label ? "img" : undefined}
      {...props}
    />
  )
}

export { AnimatedIcon }

动态图标库

悬停或键盘聚焦任意图标即可预览动画,点击会复制对应来源的按需导入语句。可以在“全部 / AnimateIcons / ItsHover”之间切换。

455 / 455 个逐路径动态图标

直接使用

import { BellRingIcon, CopyIcon } from "@animateicons/react/lucide"
import {
  AirplaneIcon,
  BrandOpenaiIcon,
} from "@/components/ui/animated-icons"

<BellRingIcon size={24} />
<CopyIcon size={24} duration={0.7} />
<AirplaneIcon size={24} />
<BrandOpenaiIcon size={24} />

每个图标默认在自身悬停时播放。如果交互区域比图标大,例如工具栏按钮或菜单项,可以使用 wui 的 AnimatedIcon 统一处理无障碍语义,并通过 ref 让父元素启动动画。

从父元素控制

"use client"

import { useRef } from "react"
import { BellRingIcon } from "@animateicons/react/lucide"

import {
  AnimatedIcon,
  type AnimatedIconHandle,
} from "@/components/ui/animated-icon"

export function Notifications() {
  const iconRef = useRef<AnimatedIconHandle>(null)

  return (
    <button
      aria-label="通知"
      onMouseEnter={() => iconRef.current?.startAnimation()}
      onMouseLeave={() => iconRef.current?.stopAnimation()}
      onFocus={() => iconRef.current?.startAnimation()}
      onBlur={() => iconRef.current?.stopAnimation()}
    >
      <AnimatedIcon ref={iconRef} icon={BellRingIcon} size={24} />
    </button>
  )
}

startAnimation()stopAnimation() 适合按钮、菜单项、保存状态等由父元素控制的场景。组件会保留每个图标自己的动画编排,不会覆盖成统一的动效模板。

减少动态效果

AnimateIcons 会读取用户的减少动态效果偏好;本地化的 ItsHover 图标通过统一导出层停止悬停和命令式播放。请从 @/components/ui/animated-icons 导入 ItsHover 图标,不要绕过入口直接引用内部文件。

图标来源

  • AnimateIcons:248 个主集合图标,MIT 许可,通过 npm 按需导入。
  • ItsHover:本地化 207 个去重后的补充图标,保留 Apache 2.0 许可证和来源说明。

品牌图标的源码许可不代表获得对应品牌的商标授权,实际产品使用前仍需核对品牌规范。

组件属性

PropTypeDefaultDescription
icon *AnimatedIconGlyphPath-animated icon imported from `@animateicons/react/lucide`.
labelstringAccessible name. Omit it when the icon is purely decorative.
refRef<AnimatedIconHandle>Imperative playback handle exposed by the animated glyph.
sizenumber
durationnumber
isAnimatedboolean