MotionButton 动效按钮
保留原生按钮和链接语义的 13 类交互与 35 个来源图标组合
安装
import { TxMotionButton } from '@talex-touch/tuffex/motion-button'
import '@talex-touch/tuffex/base.css'
import '@talex-touch/tuffex/motion-button/style.css'
用法
全部交互与来源组合
演示可筛选 13 类交互,遍历 35 个图标组合,并切换网格、列表和图标矩阵。每项支持真实悬停、键盘聚焦、原生激活和装饰回放。选中状态与激活次数只保存在演示自己的本地状态里,复制项会真的写入剪贴板。
示例加载中...
调用方的操作与内容
sourceId 只选择视觉参数,不执行操作,也不提供默认文案。悬停可以显示勾选图标,但不宣称业务成功。只有调用方设置的 selected 才启用 activeLabel。
<script setup lang="ts">
import { ref } from 'vue'
import { TxMotionButton } from '@talex-touch/tuffex/motion-button'
const copied = ref(false)
const busy = ref(false)
const error = ref('')
const marked = ref(false)
async function copyHash() {
busy.value = true
error.value = ''
try {
await navigator.clipboard.writeText('43c29ce9cdd16459e3eab4992381b8d35b38776a')
copied.value = true
}
catch {
error.value = '无法访问剪贴板'
}
finally {
busy.value = false
}
}
</script>
<template>
<TxMotionButton source-id="4" label="复制提交" active-label="已复制"
:selected="copied" :disabled="busy" @click="copyHash" />
<p role="status">{{ error }}</p>
<TxMotionButton variant="sparkle" label="标记本地条目" :selected="marked" @click="marked = !marked">
<template #icon><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M4 6h16M4 12h16M4 18h16" /></svg></template>
<template #active-icon><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5" /></svg></template>
<template #default="{ selected }">{{ selected ? '已标记本地条目' : '标记本地条目' }}</template>
</TxMotionButton>
</template>
应用负责真实业务操作和图标内容。内容插槽用于标签和装饰,需要独立操作的控件应放在按钮外。
焦点模糊链接
来源 35 渲染真实链接或按钮的分组。组件不创建占位地址,也不把多个交互元素包在一个按钮里。
<script setup lang="ts">
import { ref } from 'vue'
import { TxMotionButton } from '@talex-touch/tuffex/motion-button'
const lastLabel = ref('')
</script>
<template>
<TxMotionButton source-id="35" label="项目链接" :items="[
{ label: '仓库', href: 'https://github.com/TalexDreamSoul/talex-touch' },
{ label: '文档', href: '/docs' },
{ label: '本地操作' },
]" @select="item => lastLabel = item.label" />
<output>{{ lastLabel }}</output>
</template>
最佳实践
- 用
label、默认插槽或ariaLabel提供明确名称。图标矩阵中的控件也需要无障碍名称。 - 在
@click或@select中执行真实操作。根据调用方状态设置selected和activeLabel。悬停与回放不报告成功,也不改写业务状态。 - 导航用
href,表单提交用type="submit"。按钮保留 Enter 和空格激活,链接保留 Enter 激活及组合键打开方式。 - 操作不可执行时设置
disabled。禁用链接会移除地址和 Tab 停靠,不依靠装饰遮罩拦截事件。 - 使用
sourceId选择原始组合。内容不同时可以覆盖variant、图标和颜色,也可以通过插槽提供自己的图标。 - 焦点模糊的
#item插槽只放非交互标签。外层原生链接或按钮已经负责聚焦和激活。 - 设置
animated=false保留内容和静态端点。减少动态效果、文档隐藏、控件离屏及 KeepAlive 失活都会暂停装饰动画。
API 参考
属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
sourceId | MotionButtonSourceId | — | 原始字符串 ID "1"~"35",选择交互、图标对、活动颜色/填充和保留时间。不提供可见文案或业务操作。 |
variant | MotionButtonVariant | 来源预设,否则 morph | 显式覆盖交互类型,下方列出全部 13 个值。 |
label | string | '' | 调用方提供的可见标签。图标控件没有 ariaLabel 时也用它命名。 |
activeLabel | string | — | 仅在 selected === true 时替换 label。 |
ariaLabel | string | — | 覆盖无障碍名称。焦点模糊模式用于分组名称。 |
icon | MorphIconSource | 来源预设 | 首个图标,接受内置名称、SVG 路径 d、Lucide 风格 IconNode,以及 IconMorph 支持的 SVG 源码。 |
activeIcon | MorphIconSource | 来源预设,否则 icon | 图标对交互的目标图标。 |
iconColor | string | 来源预设,否则 currentColor | pulse/shake 的首个图标活动颜色,也是目标颜色的回退值。静止图标继承控件文字颜色。 |
activeIconColor | string | 来源预设,否则 iconColor | 目标图标颜色,接受 CSS 颜色或 token,立即切换。 |
activeFill | boolean | 来源预设,否则 false | 在 pulse/color-morph/morph 中填充活动图标。 |
size | 'xs' | 'sm' | 'md' | 'lg' | md | 高度分别为 24/30/36/42 px,沿用 TuffEx 尺寸词汇。 |
disabled | boolean | false | 原生按钮禁用,或移除链接地址和 Tab 停靠。焦点模糊模式会禁用所有条目。 |
animated | boolean | true | 共享生命周期允许时启用装饰动画。 |
selected | boolean | — | 调用方提供的目标图标状态,按钮同步 aria-pressed。不执行业务,也不确认成功。 |
href | string | — | 渲染原生链接,代替按钮。 |
target | string | — | 原生链接目标窗口。 |
rel | string | _blank 时为 noopener noreferrer | 原生链接关系,显式值优先。 |
type | 'button' | 'submit' | 'reset' | button | 原生按钮类型,不应用于链接。 |
iconOnly | boolean | false | 只显示图标的方形控件,保留无障碍名称。 |
hoverBackground | string | var(--tx-fill-color) | 交互背景立即变化,不对悬停颜色补间。 |
holdDuration | number | 来源预设,否则 0 | 离开后保留装饰目标图标的毫秒数。4/21/22/24/25 默认保留 500 ms,不保留成功文案。 |
spring | Transition | 各元素的源参数 | 显式覆盖几何弹簧。默认外层布局为 500/25,普通图标为 600/25,rotate/text-reveal 为 400/25,expand-ring 为 400/20,焦点轮廓为 350/20,通知点为 600/15。也接受 duration/ease;物理参数会传给 IconMorph。 |
magneticStrength | number | 0.35 | 磁吸的指针偏移倍数。 |
magneticRange | number | — | 可选的作用距离,单位 px。未设置时保留目录样例在按钮内不限制距离的行为。 |
magneticSpring | SpringConfig | { stiffness: 500, damping: 25 } | 磁吸位移与归零共用的逐帧弹簧。 |
items | readonly MotionButtonItem[] | [] | 焦点模糊的调用方条目。每项有 label,可选 href、target、rel 和 disabled。 |
blurAmount | number | 4 | 焦点模糊的其他条目模糊半径,单位 px。 |
opacityAmount | number | 0.4 | 其他条目的不透明度,限制在 0~1。 |
showBrackets | boolean | true | 显示活动条目的虚线轮廓。 |
id、name、value、form、download、aria-expanded 和 aria-controls 等原生属性透传到实际元素。焦点模糊模式的透传属性应用于分组。
事件
| 事件 | 参数 | 说明 |
|---|---|---|
click | MouseEvent | 普通按钮或链接的原生激活。禁用时不发出,不隐式更新状态,也不延迟业务操作。 |
select | (item: MotionButtonItem, index: number, event: MouseEvent) | 启用的焦点模糊条目被原生激活。调用方未阻止默认事件时保留导航。 |
插槽
| 插槽 | 参数 | 说明 |
|---|---|---|
default | { active, selected, disabled } | 可见标签和内容,回退为经 TextMorph 渲染的 label。空焦点模糊分组也提供此回退。 |
icon | { active, selected } | 自定义首个图标。morph/color-morph 可用单个作用域图标插槽根据活动状态渲染。 |
active-icon | { active, selected } | slide-arrow、sparkle、ring、morph 和 color-morph 的目标图标。成对插槽保留来源位移或缩放切换。 |
reveal | { active } | text-reveal 的第二行揭示文字。它属于装饰,不重复进入无障碍名称,默认仍显示同一标签。 |
item | { item, index, active } | 焦点模糊的条目标签,不放嵌套交互控件。 |
暴露方法
| 方法 | 签名 | 说明 |
|---|---|---|
replay | () => void | 回放视觉轨迹,不发出 click/select,不改写 selected。磁吸位移/归零和焦点模糊也可回放。失活或禁用时不启动工作。 |
focus | () => void | 聚焦原生控件,或焦点模糊中首个启用的条目。 |
CSS 变量
| 变量 | 默认值 | 说明 |
|---|---|---|
--tx-motion-button-height | 24/30/36/42 px | 尺寸档位的控件高度。 |
--tx-motion-button-pad | 12/16/24/28 px | 尺寸档位的水平内边距。 |
--tx-motion-button-duration | 图标/轮廓弹簧解析结果 | 图标和轮廓的几何过渡时长,失活时为零。 |
--tx-motion-button-ease | 图标/轮廓弹簧解析结果 | 图标和轮廓的几何/透明度编译曲线。 |
--tx-motion-button-layout-duration | 500/25 弹簧解析结果 | 外层内边距和缩放的过渡时长。 |
--tx-motion-button-layout-ease | 500/25 弹簧解析结果 | 独立外层几何曲线。 |
--tx-motion-button-dot-duration | 600/15 弹簧解析结果 | ring 通知点过渡时长。 |
--tx-motion-button-dot-ease | 600/15 弹簧解析结果 | 独立通知点曲线。 |
--tx-motion-button-hover-bg | hoverBackground | 立即切换的交互背景。 |
--tx-motion-button-icon-color | 解析后的目标颜色 | shake 的活动标签颜色,图标颜色和填充也立即变化。 |
--tx-motion-button-blur | blurAmount | 焦点模糊的其他条目滤镜。 |
--tx-motion-button-dim | opacityAmount | 焦点模糊的其他条目不透明度。 |
类型
导出 MotionButtonProps、MotionButtonEmits、MotionButtonInstance、MotionButtonItem、MotionButtonSize、MotionButtonVariant、MotionButtonSourceId、MotionButtonCatalogEntry 和 TxMotionButtonInstance。MotionButton 可安装,TxMotionButton 是 Vue 组件。MOTION_BUTTON_VARIANTS、MOTION_BUTTON_SOURCE_IDS、MOTION_BUTTON_CATALOG 和 MOTION_BUTTON_PRESETS 提供真实枚举与来源元数据。
| 交互 | 独立轨迹 |
|---|---|
slide-arrow | 首个图标向左退出 10 px,标签保持连续,右侧图标从右侧 10 px 进入。宽度和间距沿 600/25 弹簧变化。 |
sparkle | 首个图标向上退出 15 px 并缩至 0.8,目标从下方进入。两个星粒分别从 −45°/+45° 回转,延迟 50/100 ms。 |
morph | 各来源线条图标对使用 IconMorph 的向量几何,0.5→1 的缩放和透明度入场保留来源切换节奏。 |
color-morph | Bookmark、ThumbsUp 和 Star 各保留自己的轮廓,立即切换颜色和填充,保留来源缩放/透明度节奏。 |
pulse | Heart 在 400 ms 内按 1→1.25→1 脉冲缩放,活动填充和颜色立即变化。 |
rotate | Settings 或 RefreshCw 图标沿 400/25 弹簧旋转 180°,离开时归零。 |
shake | Trash2 在 400 ms 内按 0/−2/0/−2/0 px 上下移动,同时按 0/−10°/10°/−10°/0° 摇动。 |
ring | Bell/BellRing 按 −15°/15° 和 0.8 倍缩放切换。独立的 6 px 通知点延迟 100 ms,沿自己的 600/15 弹簧弹出。 |
glare | 50 px、−20° 的光泽在 850 ms 内从 −150% 扫到 150%,间隔 1 s,仅交互时循环。 |
text-reveal | 箭头旋转 45°,两个 18 px 文本行沿 400/25 弹簧向上移动一行。 |
magnetic | 指针偏移乘以 strength 拉动控件,单一共享弹簧保留速度并回到同一原点。 |
expand-ring | 图标缩放至 1.1,独立轮廓在 600 ms 内从 1 扩散至 1.15,并淡出。 |
focus-blur | 其他真实链接/按钮模糊并降低透明度,活动条目的虚线轮廓沿 350/20 弹簧从 1.3 缩放至 1.1。 |
概述
- 显式
variant、图标、颜色和holdDuration优先于sourceId。来源标签只用于归属信息,不自动变成应用文案。 - 悬停、聚焦、指针按下和回放驱动装饰。
selected保留目标图标并启用activeLabel,始终由调用方提供。来源实现中仅凭悬停宣称“已复制”的行为不沿用。 - 内容和装饰层不拦指针事件。普通控件使用原生按钮/链接,焦点模糊的各条目也保留原生语义和可见键盘轮廓。禁用链接没有
href、Tab 停靠或激活事件。 - 原生表单提交/重置和链接导航不改成 JavaScript 键盘模拟。Enter/空格按下装饰遵循实际元素支持的按键。
- 共享活动边界停止 CSS 循环、待执行回放帧、保留/回放定时器、磁吸 RAF 和图标/文字变形。SSR 不读取浏览器 API。减少动态效果时保留可读内容与静态端点。
- 回放只处理视觉:先绘制静止姿态,再进入同一轨迹。磁吸回放只读一次控件尺寸,复用拉动和归零弹簧;焦点模糊回放强调首个启用条目。
技术实现
- 上游:Amicro,MIT,Copyright (c) 2026 SYED SUBHAN UDDIN。
- 行为来源:
src/components/AnimatedButton.tsx:39~415、src/data/buttons.tsx:43~77和src/components/cards/FocusBlur.tsx:17~73。已阅读 registry 的hover/magnetic-button.tsx与hover/glow-button.tsx,独立 registry 版本归入 Motion 交互组件族。 - 原始图标来自上游锁文件固定的 lucide-react 0.546.0。全部 46 个 SVG 图标保留原始节点和几何属性,仅移除 React key。ISC 与 Feather 派生部分的 MIT 声明完整保存在
icons.ts的@license头中。 - TuffEx 落点:
motion-button/src/TxMotionButton.vue、catalog.ts、icons.ts和MotionButtonGlyph.vue。图标变形复用 IconMorph,文字值变化复用 TextMorph,物理与生命周期复用已有共享弹簧和useMotionActivity。活动边界变化时以实例 key 销毁旧变形控制器,不只改变减少动态效果标志。 - 下表保留全部 35 个实际组合。ID 改变图标、轨迹参数、填充和保留时间,不只是更换标签。固定标签属于来源元数据,可见文案由调用方提供。
| 来源 ID | 来源组合 | 交互 | 图标 | 来源 |
|---|---|---|---|---|
1 | Download for Mac | slide-arrow | Apple → ArrowRight | buttons.tsx:43 |
2 | Star on GitHub | sparkle | GitHub → Star | buttons.tsx:44 |
3 | Deploy App | morph | Cloud → CloudUpload | buttons.tsx:45 |
4 | Copy Hash | morph | Copy → Check;保留图标 500 ms | buttons.tsx:46 |
5 | Sponsor | pulse | Heart;活动填充 | buttons.tsx:47 |
6 | Share | morph | Link → Send | buttons.tsx:48 |
7 | Preview | morph | Play → Pause | buttons.tsx:49 |
8 | Settings | rotate | Settings | buttons.tsx:50 |
9 | Delete | shake | Trash2 | buttons.tsx:51 |
10 | Subscribe | ring | Bell → BellRing | buttons.tsx:52 |
11 | Search | morph | Search → X | buttons.tsx:53 |
12 | Theme | morph | Moon → Sun | buttons.tsx:54 |
13 | Microphone | morph | Mic → MicOff | buttons.tsx:55 |
14 | Camera | morph | Video → VideoOff | buttons.tsx:56 |
15 | Volume | morph | Volume2 → VolumeX | buttons.tsx:57 |
16 | Lock | morph | Lock → Unlock | buttons.tsx:58 |
17 | Directory | morph | Folder → FolderOpen | buttons.tsx:59 |
18 | Visibility | morph | Eye → EyeOff | buttons.tsx:60 |
19 | Save Later | color-morph | Bookmark 轮廓 → 填充 | buttons.tsx:61 |
20 | Like | color-morph | ThumbsUp 轮廓 → 填充 | buttons.tsx:62 |
21 | Download | morph | Download → Check;保留图标 500 ms | buttons.tsx:63 |
22 | Upload | morph | Upload → Check;保留图标 500 ms | buttons.tsx:64 |
23 | Account | morph | User → UserCheck | buttons.tsx:65 |
24 | Submit | morph | Send → Check;保留图标 500 ms | buttons.tsx:66 |
25 | Edit | morph | Pen → Check;保留图标 500 ms | buttons.tsx:67 |
26 | Network | morph | Wifi → WifiOff | buttons.tsx:68 |
27 | Power | morph | Battery → BatteryCharging | buttons.tsx:69 |
28 | Expand | morph | Maximize → Minimize | buttons.tsx:70 |
29 | Reload | rotate | RefreshCw | buttons.tsx:71 |
30 | Favorite | color-morph | Star 轮廓 → 填充 | buttons.tsx:72 |
31 | Glare Shine | glare | Star 与扫过的光泽 | buttons.tsx:73 |
32 | Text Reveal | text-reveal | ArrowRight 与两个文本行 | buttons.tsx:74 |
33 | Magnetic Field | magnetic | GitHub 与真实指针拉动 | buttons.tsx:75 |
34 | Expand Ring | expand-ring | Link 与独立扩散轮廓 | buttons.tsx:76 |
35 | Focus Blur Links | focus-blur | 调用方提供的链接/按钮分组 | buttons.tsx:77 |
查看源码
packages/tuffex/packages/components/src/motion-button/index.ts