---
title: "MotionForm"
description: "基于 TuffEx 表单控件的十五种动效交互"
category: Advanced
status: beta
since: 0.6.3
tags: [form, motion, validation, otp, files]
syncStatus: documented
verified: false
---

## 用法

### 全部变体
`validate` 只发起请求；调用方写回 `status` 与 `error`，改变 `validationKey` 重播震动。
:::TuffDemoWrapper{demo="MotionFormDemo" code-lang="vue"}
---
code: |
  <script setup lang="ts">
  import type { MotionFormStatus, MotionFormValue } from '@talex-touch/tuffex/motion-form'
  import { TxMotionForm } from '@talex-touch/tuffex/motion-form'
  import { ref } from 'vue'

  const email = ref('')
  const status = ref<MotionFormStatus>('default')
  const error = ref('')
  const validationKey = ref(0)
  function validate(value: MotionFormValue) {
    const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value))
    status.value = valid ? 'success' : 'error'
    error.value = valid ? '' : '请输入有效邮箱。'
    validationKey.value++
  }
  </script>

  <template>
    <TxMotionForm
      v-model="email"
      variant="error-shake"
      label="邮箱"
      :status="status"
      :error="error"
      :validation-key="validationKey"
      :labels="{ validate: '校验', verified: '已通过校验' }"
      @validate="validate"
    />
  </template>
---
:::

### 最佳实践

- 模型、校验消息与业务状态由调用方维护；组件不自行判定成功，也不定时清除错误。
- `filesSelected` 只表示选中文件：用应用自己的服务上传每个 `File`，再用结果驱动 `status`。
- 始终提供 `label` 与本地化的 `labels`；OTP 每格的名称也由 `label` 生成。
- `readonly` 只用于文本类字段与 OTP；选择、文件、滑块与动作用 `disabled`。
- 只有外层原生表单负责提交时才设 `nativeType="submit"`，不要在点击与表单 submit 中重复执行同一操作。

## API 参考

### 属性

| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `variant` | `MotionFormVariant` | `'floating-label-input'` | 十五个效果之一，见下方变体表。 |
| `modelValue` | `MotionFormValue` | 未设置 | 受控值，形状由变体决定。 |
| `label` | `string` | `''` | 可见标签与无障碍名称；为空时用 `labels.field`。 |
| `placeholder` | `string` | `''` | 文本与选择的占位；浮动标签静止时会遮住它。 |
| `description` | `string` | `''` | 消息区的辅助文案；错误与状态优先。 |
| `size` | `'xs' \| 'sm' \| 'md' \| 'lg'` | `'md'` | 高度、选择标记与圆角；动作按钮的 `xs` 对应 Button 的 `sm`。 |
| `disabled` | `boolean` | `false` | 禁止编辑、选择、移除、显示密码与动作请求。 |
| `readonly` | `boolean` | `false` | 文本、文本域与 OTP 只读；仍可显示密码。 |
| `required` | `boolean` | `false` | 必填标记与无障碍状态；原生文本字段同时设置 `required`。 |
| `status` | `'default' \| 'loading' \| 'success' \| 'error'` | `'default'` | 调用方驱动的状态；`loading` 时阻止动作请求。 |
| `error` | `string` | `''` | 非空时进入错误状态并显示该消息。 |
| `validationKey` | `string \| number` | 未设置 | 变化时重播当前错误。 |
| `labels` | `Partial<MotionFormLabels>` | 未设置 | 本地化静态文案、数字框名称与移除标签。 |
| `options` | `MotionFormOption[]` | `[]` | 单选、下拉与标签的选项。 |
| `inputType` | `'text' \| 'email' \| 'number' \| 'date'` | `'text'` | 文本控件类型；密码变体自行切换 text/password。 |
| `autocomplete` | `string` | 未设置 | 原生自动完成；OTP 首格固定为 `one-time-code`。 |
| `passwordVisible` | `boolean` | 未设置 | 受控的密码显示状态，配合 `update:passwordVisible`。 |
| `rows` | `number` | `2` | 文本域最小行数。 |
| `maxRows` | `number` | `8` | 自动增高的最大行数，超出后滚动；不小于 `rows`。 |
| `maxLength` | `number` | 未设置 | 文本与文本域的长度上限；OTP 固定 4 位。 |
| `accept` | `string` | `'*/*'` | 文件选择与拖放的类型过滤。 |
| `multiple` | `boolean` | `true` | 多选文件；为 `false` 时新文件替换旧文件。 |
| `maxFiles` | `number` | `10` | 文件数量上限。 |
| `min` | `number` | `0` | 滑块最小值。 |
| `max` | `number` | `100` | 滑块最大值。 |
| `step` | `number` | `1` | 滑块步长。 |
| `formatValue` | `(value: number) => string` | 未设置 | 滑块数值与提示的格式化函数。 |
| `nativeType` | `'button' \| 'submit'` | `'button'` | 提交按钮的原生类型。 |
| `motion` | `boolean` | `true` | 启用动效；仍受可见性、KeepAlive 与减少动态效果限制。 |

### 事件

| 事件 | 参数 | 含义 |
| --- | --- | --- |
| `update:modelValue` | `MotionFormValue` | 用户编辑或选择时触发。 |
| `change` | `MotionFormValue` | 与 `update:modelValue` 同时触发。 |
| `update:passwordVisible` | `boolean` | 切换密码显示时触发。 |
| `focus`, `blur` | `FocusEvent` | 焦点进入或离开组件时触发；内部移动不触发。 |
| `search` | `string` | search-expand 的文本变化时触发；不发请求。 |
| `validate` | `MotionFormValue` | 请求调用方校验当前值。 |
| `submit` | `[value: MotionFormValue \| undefined, event: MouseEvent]` | 请求调用方执行操作；不修改 `status`。 |
| `otpComplete` | `string` | 四格都填入数字时触发；改写后可再次触发。 |
| `filesSelected` | `FileUploaderFile[]` | 本次新增并被接受的文件；不是累计列表，也不是上传完成。 |
| `fileRemove` | `{ id: string, value: FileUploaderFile[] }` | 移除文件时触发，携带 ID 与新列表。 |

### 插槽

| 插槽 | 作用域 | 用途 |
| --- | --- | --- |
| `label` | 无 | 可见标签，通过稳定 ID 关联控件。 |
| `prefix`, `suffix` | 无 | 输入前后缀；`suffix` 会替换密码显示按钮。 |
| `option` | `{ option, selected }` | 单选与下拉的选项内容。 |
| `chip` | `{ option }` | 标签文案；移除按钮保留。 |
| `file` | `{ file, remove: () => void }` | 文件行内容；移除按钮保留。 |
| `submit` | `{ status: MotionFormStatus, label: string }` | 提交按钮文案，与状态图标并排。 |
| `status` | `{ status, error, value }` | 实时消息区的内容。 |

### 暴露方法

| 方法 | 行为 |
| --- | --- |
| `focus()` | 聚焦第一个可用的输入、文本域或动作按钮。 |
| `blur()` | 让当前聚焦的子控件失焦。 |

### 类型

```ts
import type { FileUploaderFile } from '@talex-touch/tuffex/file-uploader'
import type { MotionFormProps, MotionFormVariant } from '@talex-touch/tuffex/motion-form'

type MotionFormValue = string | number | boolean | (string | number)[] | FileUploaderFile[]
type MotionFormOption = TxSelectOption // { value, label, disabled?, icon?, description? }
// FileUploaderFile: { id, name, size, type, file: File }
// MOTION_FORM_VARIANTS 导出完整只读变体元组。
```

#### MotionFormVariant

| 原始 ID | `variant` | 模型 | 效果 |
| --- | --- | --- | --- |
| `frm1` | `floating-label-input` | `string` 或 `number` | 聚焦或有值时标签上浮缩小。 |
| `frm2` | `input-focus-glow` | `string` 或 `number` | 聚焦时扩散光环，失焦收回。 |
| `frm3` | `password-toggle` | `string` | 显示按钮切换明文，眼睛图标旋转；不改值。 |
| `frm4` | `search-expand` | `string` | 聚焦时在可用宽度内展开，图标位移。 |
| `frm5` | `checkbox-draw` | `boolean` | 勾选时绘出勾并轻微放大。 |
| `frm6` | `radio-scale` | `string` 或 `number` | 单选，中心圆点弹性缩放。 |
| `frm7` | `error-shake` | `string` 或 `number` | `error`、错误状态或 `validationKey` 变化时震动。 |
| `frm8` | `success-check` | `string` 或 `number` | 请求校验；成功状态弹出并绘出勾。 |
| `frm9` | `select-dropdown` | `string` 或 `number` | Select 按锚点方向缩放、位移并淡入。 |
| `frm10` | `multi-select-chips` | `(string \| number)[]` | 已选标签弹性进出，移除按钮更新数组。 |
| `frm11` | `textarea-auto-grow` | `string` | 按实际滚动高度弹性增高，有上限。 |
| `frm12` | `otp-input` | `string` | 四格数字自动前进，粘贴内容自动分发。 |
| `frm13` | `file-upload-dropzone` | `FileUploaderFile[]` | 选择或拖放文件；拖入时边框脉动、箭头上抬。 |
| `frm14` | `range-slider` | `number` | Slider 处理指针与键盘，拇指弹性并浮动显示数值。 |
| `frm15` | `form-submit-button` | 可选 `MotionFormValue` | 按调用方状态切换文字、转圈与勾。 |

#### MotionFormLabels

默认值导出为 `MOTION_FORM_DEFAULT_LABELS`，`labels` 可局部覆盖。

| 键 | 默认值/类型 |
| --- | --- |
| `field` | `'Field'` |
| `showPassword`, `hidePassword`, `capsLock` | `'Show password'`, `'Hide password'`, `'CapsLock is on'` |
| `validate`, `validating`, `verified`, `validationError` | `'Validate'`, `'Validating…'`, `'Verified'`, `'Validation failed'` |
| `submit`, `submitting`, `submitted`, `submitError` | `'Submit'`, `'Submitting…'`, `'Submitted'`, `'Submission failed'` |
| `chooseFiles`, `dropFiles`, `fileHint` | `'Choose files'`, `'Drop files here'`, `'or click to browse'` |
| `searchOptions`, `noOptions` | `'Search options'`, `'No options'` |
| `otpDigit` | `(index: number) => string`；从 1 开始，默认 `Digit N of 4` |
| `removeOption` | `(label: string) => string`；默认 `Remove <label>` |
| `removeFile` | `(name: string) => string`；默认 `Remove <name>` |

### CSS 变量

| 变量 | `md` 默认值 | 用途 |
| --- | --- | --- |
| `--tx-mf-height` | `36px` | 输入与动作高度；各尺寸为 26/30/36/42px。 |
| `--tx-mf-choice` | `22px` | 复选与单选标记尺寸；各尺寸为 16/18/22/24px。 |
| `--tx-mf-radius` | `12px` | 字段圆角；各尺寸为 8/10/12/12px。 |

颜色来自 `--tx-color-*`、`--tx-text-color-*`、`--tx-bg-color` 与 `--tx-border-color-*`；动效变量由内部生成，不是公开 API。

## 概述

- 编辑、选项、拖放与键盘语义由 Input、Select、Checkbox、Radio、Textarea、FileUploader 与 Slider 负责；本组件只添加动效与受控事件。
- 标签与控件相邻，`useId` 关联标签、辅助文案与 OTP；密码按钮是带 `aria-pressed` 的原生按钮。
- 状态与错误显示在 `role="status" aria-live="polite"` 区域；重播动效不重置焦点。
- OTP 支持粘贴、自动填充、←/→、Home/End、Backspace 与 Delete；外部传入的新值替换全部四格。
- 标签变体每次添加一个选项，已选项不能重复添加。
- 不可见或 KeepAlive 失活时暂停动效与监听；减少动态效果只关闭装饰动效，不影响输入与测量。

## 技术实现

- 文字过渡用 `TxTextMorph`，曲线来自共享的 liquid spring resolver；上游的定时清除错误与模拟提交成功改为调用方维护的 `error` 与 `status`。
- 上游：[Amicro 提交 43c29ce](https://github.com/Subhan-code/Amicro--Micro-transitions-/tree/43c29ce9cdd16459e3eab4992381b8d35b38776a) 的 `src/components/forms/AnimatedFormElement.tsx`，MIT，Copyright (c) 2026 SYED  SUBHAN UDDIN。
- 源码：`packages/tuffex/packages/components/src/motion-form/`。

<TuffDocSourceLink />
