---
title: FlipOverlay 翻转遮罩
description: 从触发源翻转展开的 3D Overlay
category: Effects
status: beta
since: 0.3.4
tags: [overlay, motion, transition]
syncStatus: reviewed
verified: true
---

## 用法

### 基础
`source` 指定翻转起点；`headerTitle` 与 `headerDesc` 填充内置头部。
:::TuffDemoWrapper{demo="FlipOverlayFlipOverlayDemo" code-lang="vue"}
---
code: |
  <script setup lang="ts">
  import { ref } from 'vue'

  const open = ref(false)
  const triggerRef = ref()
  </script>

  <template>
    <TxButton ref="triggerRef" @click="open = true">打开 Overlay</TxButton>
    <TxFlipOverlay
      v-model="open"
      :source="triggerRef?.$el"
      header-title="详情视图"
      header-desc="标题、副标题与圆形关闭按钮"
    >
      <template #default="{ close }">
        <p>内容从触发源翻转展开。</p>
        <TxButton size="sm" @click="close">关闭</TxButton>
      </template>
    </TxFlipOverlay>
  </template>
---
:::

### 最佳实践

- 把真实触发元素或它的 `DOMRect` 传给 `source`；`null` 会退化为没有起点的居中卡片。
- 堆叠时各层的 `duration` 保持接近默认值，让共享遮罩与卡片动效同步。
- 尺寸约束（`width`、`maxHeight`）放进 `cardStyle`，可复用的视觉变体放进 `cardClass`。
- 常规卡片用 `surface="mask"`；`glass` / `refraction` 只在背景仍可读时用，完全自定义卡片时用 `pure`。
- 优先用 `#header-display`、`#header-actions`、`#header-close`；`#header` 会连同关闭布局一起替换。

## API 参考

### 属性

| 属性名 | 类型 | 默认值 | 说明 |
|------|------|---------|------|
| `modelValue` | `boolean` | `false` | 是否显示，配合 `v-model`。 |
| `source` | `HTMLElement \| DOMRect \| null` | `null` | 动画起点。 |
| `sourceRadius` | `string \| null` | `null` | 起点圆角。 |
| `duration` | `number` | `480` | 动画时长（ms）。 |
| `perspective` | `number` | `1200` | 3D 透视距离。 |
| `rotateX` | `number` | `6` | X 轴旋转角度。 |
| `rotateY` | `number` | `8` | Y 轴旋转角度。 |
| `randomTilt` | `boolean` | `true` | 每次打开随机轻微倾斜。 |
| `tiltRange` | `number` | `2` | 随机倾斜范围。 |
| `easeOut` | `string` | `'back.out(1.25)'` | 打开缓动。 |
| `easeIn` | `string` | `'back.in(1)'` | 关闭缓动。 |
| `maskClosable` | `boolean` | `true` | 点击遮罩或按 Escape 时关闭。 |
| `preventAccidentalClose` | `boolean` | `false` | 拦截遮罩关闭与页面退出，并闪红光警示。 |
| `globalMask` | `boolean` | `true` | 渲染 body 级共享遮罩。 |
| `surface` | `'pure' \| 'mask' \| 'blur' \| 'glass' \| 'refraction'` | `'mask'` | 内置卡片背景。 |
| `surfaceColor` | `string` | `''` | 背景基础色；默认跟随主题。 |
| `surfaceOpacity` | `number` | `0.96` | 背景不透明度（`mask` 模式）。 |
| `speedBoost` | `number` | `1.12` | 进度超过 `speedBoostAt` 后的加速倍率。 |
| `speedBoostAt` | `number` | `0.7` | 启用 `speedBoost` 的动画进度阈值。 |
| `transitionName` | `string` | `'TxFlipOverlay-Mask'` | 遮罩的 Vue transition 名称。 |
| `header` | `boolean` | `true` | 渲染内置头部；提供 `#header` 时无效。 |
| `headerTitle` | `string` | `''` | 内置头部标题，关联 `aria-labelledby`。 |
| `headerDesc` | `string` | `''` | 内置头部描述，关联 `aria-describedby`。 |
| `closable` | `boolean` | `true` | 显示关闭区（含 `#header-close`）。 |
| `closeAriaLabel` | `string` | `'Close'` | 关闭按钮的 `aria-label`。 |
| `maskClass` | `string` | `''` | 遮罩 class。 |
| `cardClass` | `string` | `''` | 卡片 class。 |
| `cardStyle` | `CSSProperties` | - | 卡片内联样式。 |
| `border` | `'solid' \| 'dashed' \| 'dash' \| 'none'` | `'solid'` | 卡片边框；`dash` 等同 `dashed`。 |
| `scrollable` | `boolean` | `true` | 内容区内部滚动。 |
| `expanded` | `boolean` | - | 受控的展开动画状态，供外层 UI 同步。 |
| `animating` | `boolean` | - | 受控的动画状态，供外层 UI 同步。 |

### 事件

| 事件名 | 参数 | 说明 |
|------|------|------|
| `update:modelValue` | `(value: boolean)` | 组件自行关闭时以 `false` 触发。 |
| `open` | - | 打开动画开始。 |
| `opened` | - | 打开动画结束。 |
| `close` | - | 关闭动画开始。 |
| `closed` | - | 关闭动画结束。 |
| `update:expanded` | `(value: boolean)` | 同步 `expanded`。 |
| `update:animating` | `(value: boolean)` | 同步 `animating`。 |

### 插槽

| 插槽 | 参数 | 说明 |
|------|------|------|
| `default` | `{ close, expanded, animating, closable, headerTitle, headerDesc }` | 内容区。 |
| `header` | `{ close, expanded, animating, closable, headerTitle, headerDesc }` | 替换整个内置头部。 |
| `header-display` | `{ close, expanded, animating, closable, headerTitle, headerDesc }` | 标题与描述区。 |
| `header-actions` | `{ close, expanded, animating, closable, headerTitle, headerDesc }` | 关闭按钮左侧的操作区。 |
| `header-close` | `{ close, expanded, animating, closable, headerTitle, headerDesc }` | 关闭区；`closable=false` 时不渲染。 |

### 暴露方法

| 方法 | 类型 | 说明 |
|------|------|------|
| `close()` | `() => void` | 运行完整关闭动画并派发 `update:modelValue(false)`。 |

## 概述

- 浮层 Teleport 到 `<body>`；非 prop 属性落在遮罩上。
- 关闭顺序为 `close` → `update:modelValue(false)` → `closed`；父级回写 `v-model` 后才完全关闭。
- 遮罩点击与 Escape 受 `maskClosable` 约束，`preventAccidentalClose` 时改为闪烁警示；关闭按钮与 `close()` 不受这两项限制。
- 头部优先级：`#header` 覆盖内置头部；否则由 `header` 决定是否渲染。
- `globalMask` 下堆叠的浮层共享遮罩，只有顶层响应点击；尺寸相近的相邻层错位（最多 3 层），更深层逐级淡出。
- 卡片是 `role="dialog"`，带 `aria-modal="true"`；打开时焦点移入卡片，关闭后回到打开前的元素。

## 技术实现

- 翻转动画由按需加载的 GSAP 补间驱动，见 `flip-overlay-motion.ts`。
- 源码：`packages/tuffex/packages/components/src/flip-overlay/`。

<TuffDocSourceLink />
