这是一款用 React 19 + Motion(原 Framer Motion)+ Tailwind CSS 4 搭出来的滤镜标签组件:四枚圆角标签「卡通、像素、水彩、随机」水平排布,每枚左侧是 Lucide 图标;选中的那一枚会向右横向展开、文字以淡入方式出现,其他标签则同步收缩回图标状态。全部宽度变化不写一行 keyframes,靠 Motion 的 `layout` 属性自动测量前后布局并以弹簧曲线过渡,视觉上像一枚会呼吸的胶囊。整段用 React.createElement 表达而非 JSX,便于直接放进 CDN + ESM 的极简项目里。
这款滤镜标签组件的四个亮点
切换动画用 `motion.div` 的 `layout` 布尔属性触发 FLIP:Motion 内部会在渲染前后各测量一次盒子位置和尺寸,用 CSS transform 反向补偿再解开,性能远优于每帧改 width。
过渡曲线只写了 `{ type: "spring", visualDuration: 0.3, bounce: 0.3 }`——`visualDuration` 是 Motion 独有的物理感时长参数,比传统的 duration 更符合视觉感知。
文字出现/消失用 `AnimatePresence mode="popLayout"` 包裹,配合 `initial: {opacity:0}` / `exit: {opacity:0}`,收起时不会顶掉当前布局,切换手感非常干净。
无障碍不打折:容器写了 `role="radiogroup"`,每枚是隐藏的 `` 加可点 `
Motion Layout 与 AnimatePresence 的三个门道
其一,`layout` 属性同时挂在外层 `motion.div` 和内层 `motion.span`(图标)上,是为了让图标在标签横向扩展时也能被独立跟踪,否则图标位置会在动画中间跳一下。其二,`mode="popLayout"` 与默认 `sync` 的区别是「先退场再入场」而非同时进行——对于同一位置的元素切换特别关键,否则会看到旧文字和新宽度错位。其三,Tailwind 的原子类 `text-rose-500 bg-rose-200/75` 直接切颜色,而不是走 CSS 过渡;颜色过渡靠元素本身的 `transition-colors` 兜底,避免和 Motion 的布局动画抢主线程。源码中均有注释,可对照 `AnimatePresence` 部分展开阅读。
要改数据或替换图标怎么下手
四枚标签的数据在 `ITEMS` 数组(约第 11 行),把 name 换成中文、icon 从 Lucide 里选新的即可;Lucide 图标全量列表在 lucide.dev。要增删数量,直接改数组长度,不用改任何布局代码。要换主色,把 Tailwind 里的 `rose-500` / `rose-300` / `rose-200/75` 全局替换成想要的色阶(比如 `indigo` / `emerald`)。默认选中项写在 `useState(3)`,改成 0 就是初始高亮第一个。
浏览器兼容性:需支持 ES Modules 与 CSS `border-radius`;React 19 通过 esm.sh CDN 直接加载,无需构建工具;生产上建议本地打包 React 与 Motion 以减少首屏请求。详细运行方法请参见压缩包内的帮助文档。
常见问题
能改成多选而不是单选吗
可以。把 `useState` 改成一个 Set,`onChange` 里判断有则删无则加,`checked` 判断改为 `activeSet.has(i)`,样式规则保持不变即可。
不用 Tailwind CDN 会不会更快
会。CDN 版每次都会在浏览器里解析源码类名,生产环境建议改用 tailwindcss 官方 CLI 预编译成静态 CSS,只保留用到的类,首屏体积可减小 90% 以上。
怎么让选中态的动画更明显
把 spring 的 `bounce` 从 0.3 提到 0.5、`visualDuration` 从 0.3 提到 0.45,标签会更「Q 弹」;反过来想要沉稳,把 bounce 降到 0.15。
要把它接进 Next.js 或 Vite 项目怎么做
把 CDN 的 esm.sh 引用改成 `npm i react motion @tailwindcss/postcss lucide-react` 后从 node_modules 导入即可,逻辑不用改,`React.createElement` 也可以直接换成 JSX。
适合放在哪些页面
适合图像/AI 生成类应用的风格选择器、电商筛选栏、订阅套餐切换、后台管理系统的视图切换、任何需要「一组互斥选项 + 视觉反馈明显」的场景。下载源码后可作为独立组件直接嵌入 React 项目。