# @daformat/react-swipeable-cards 中文文档

规范地址：https://npmcn.com/package/%40daformat/react-swipeable-cards/

版本：1\.0\.4
npm 元数据更新时间：2026-05-19T09:11:02.099Z
中文译文更新时间：2026-11-11T08:41:45.931Z
软件包许可：Zero-Clause BSD

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40daformat/react-swipeable-cards
原文对照：https://npmcn.com/package/%40daformat/react-swipeable-cards/?lang=original
项目主页：https://github.com/daformat/react-swipeable-cards
源码仓库：https://github.com/daformat/react-swipeable-cards

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：125 kB，15 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：0 个
- peer 依赖：react \>=18
- 首次发布：2026-05-03T13:24:11.147Z
- 最新版本发布：2026-05-19T09:11:01.982Z
- 近 30 天下载量：9858（2026-10-11 至 2026-11-09，npm 官方下载统计）
- 已发布版本数：4（近 12 个月 4 个）

## 中文简介

无头反应可刷卡自旋组件,类似于 Tinder, 没有依赖性

## 安装

```sh
npm install @daformat/react-swipeable-cards
```

也可以使用：`pnpm add @daformat/react-swipeable-cards`、`yarn add @daformat/react-swipeable-cards`、`bun add @daformat/react-swipeable-cards`

## README

# 反应可刷卡

![NPM Version](https://img.shields.io/npm/v/%40daformat%2Freact-swipeable-cards) ![NPM Downloads](https://img.shields.io/npm/dm/%40daformat%2Freact-swipeable-cards)  
[![Follow daformat on GitHub](https://img.shields.io/github/followers/daformat?label=Follow%20%40daformat&style=social)](https://github.com/daformat) [![Follow daformat on X](https://img.shields.io/twitter/follow/daformat?label=Follow%20%40daformat&style=social)](https://twitter.com/daformat)

无头反应可刷卡旋转木马组件,类似于Tinder,没有 运行时间依赖。 拖放,或按程序刷卡 左,右, 上下两相. 可在屏幕外丢弃卡片或将卡片发到堆栈后端 永远循环。 由网络动画 API 所建,用于平滑的,有动力的认知 动画和中枢加权自转,在指下感觉自然.

## 安装

```bash
npm install @daformat/react-swipeable-cards
```

```bash
yarn add @daformat/react-swipeable-cards
```

```bash
pnpm add @daformat/react-swipeable-cards
```

```bash
bun add @daformat/react-swipeable-cards
```

```bash
deno add npm:@daformat/react-swipeable-cards
```

## 演示

[https://hello-mat.com/design-engineering/组件/可刷卡](https://hello-mat.com/design-engineering/component/swipeable-cards)

## 快开始

```tsx
import { SwipeableCards } from "@daformat/react-swipeable-cards";

const cards = [
  { id: "1", card: <div className="card">Card 1</div> },
  { id: "2", card: <div className="card">Card 2</div> },
  { id: "3", card: <div className="card">Card 3</div> },
];

export function App() {
  return (
    <SwipeableCards.Root
      cards={cards}
      emptyView={<div>No more cards</div>}
      onSwipe={(direction, cardId) => {
        console.log(`Swiped ${cardId} ${direction}`);
      }}
    >
      <SwipeableCards.Cards />
      <SwipeableCards.SwipeLeftButton>👎</SwipeableCards.SwipeLeftButton>
      <SwipeableCards.SwipeRightButton>👍</SwipeableCards.SwipeRightButton>
    </SwipeableCards.Root>
  );
}
```

构成部分是 **无头**: 盒子里没有东西是样式的。 你决定 如何看牌,你决定如何铺出堆栈(典型的是CSS) 组件所暴露的自定义属性,参见 [CSS 自定义属性](https://npmcn.com/package/%40daformat/react-swipeable-cards/#readme-css-custom-properties)) (中文(简体) ).

## 样式示例

考虑到以下jsx:

```tsx
// full source: https://github.com/daformat/hello-mat/blob/master/pages/design-engineering/component/swipeable-cards.tsx

<SwipeableCards.Root
  cards={[...cards /* omitted for brevity */]}
  className={styles.cards_root}
  data-style={"stacked-offset" /* "stacked-rotation" | "minimal" */}
  swipeStyle={"sendToBack"}
  sendToBackMargin={16}
  loop
>
  <SwipeableCards.Cards
    visibleStackLength={4}
    style={{ aspectRatio: "650 / 400" }}
  />
</SwipeableCards.Root>
```

我用这个来形容 [演示](https://hello-mat.com/design-engineering/component/swipeable-cards)

```scss
/* full source: https://github.com/daformat/hello-mat/blob/master/components/SwipeableCards/SwipeableCards.module.scss */

.cards_root {
  --ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1);

  [data-swipeable-cards-cards] {
    display: grid;
    grid-template-columns: 1fr;
    place-content: center;
    position: relative;
    touch-action: none;
    z-index: 1;

    [data-swipeable-cards-card-wrapper] {
      align-content: center;
      align-self: center;
      grid-area: 1 / 1;
      opacity: calc(
        1 - clamp(0, var(--stack-index0) - var(--visible-stack-length), 1)
      );
      touch-action: none;
      transform-origin: center 0;
      transition: all 0.2s var(--ease-out-cubic);
      transition-property: opacity, scale, padding-top, margin-top;
      width: 100%;
      will-change: opacity, scale, padding-top, margin-top, transform;
    }
  }

  &:is([data-style="stacked-offset"], [data-style="stacked-rotation"]) {
    [data-swipeable-cards-cards] {
      [data-swipeable-cards-card-wrapper] {
        scale: calc(
          100% - min(var(--stack-index0), var(--visible-stack-length)) * 10%
        );
      }
    }
  }

  &[data-style="stacked-offset"] {
    [data-swipeable-cards-cards] {
      [data-swipeable-cards-card-wrapper] {
        // Pick whatever vertical step you want between stacked cards.
        --card-top-distance: clamp(16px, 1vw, 32px);
        --p: calc(
          var(--card-top-distance) *
            max(var(--visible-stack-length) - var(--stack-index0), 0)
        );
        --m: calc(
          var(--card-top-distance) *
            (
              var(--visible-stack-length) - max(
                  var(--visible-stack-length) - var(--stack-index0),
                  0
                )
            )
        );
        margin-top: calc(var(--m) * -1);
        padding-top: calc(var(--p));
      }
    }
  }
}
```

## 构成部分结构

```tsx
/* Provides context to all swipeable card components */
<SwipeableCards.Root cards={cards} emptyView={...}>
  {/* The stack container */}
  <SwipeableCards.Cards>
    {/* Optional: bring your own renderer; otherwise the default renders one CardWrapper per card */}
    {(stack) =>
      stack.map((card) => (
        <SwipeableCards.CardWrapper key={card.id} card={card} />
      ))
    }
  </SwipeableCards.Cards>
  {/* Optional programmatic swipe buttons */}
  <SwipeableCards.SwipeLeftButton />
  <SwipeableCards.SwipeRightButton />
  <SwipeableCards.SwipeUpButton />
  <SwipeableCards.SwipeDownButton />
</SwipeableCards.Root>
```

## 构成部分

### `SwipeableCards.Root`

最外道的包装 提供上下文, 设置拖曳状态, 并听 改为 `pointermove` 编号 : `pointerup` 在文档上。 显示 a `<div>`。 。 。 .

这个 `loop` 道克是受歧视的结合的一部分: `loop` 实值 `false` (或 省略了) 你 **必须** 通过 `emptyView`; 当 `loop` 实值 `true` 你们 **绝对不能** 通过 `emptyView`。 。 。 .

| Prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `cards` (必须) | `CardWithId[]` | —— 说 | 堆起的首发牌. 最后一件是顶卡. |
| `emptyView` | `ReactNode` | —— 说 | 需要时间 `loop` 实值 `false`。当堆栈为空时显示. |
| `loop` | `boolean` | `false` | 何时 `true`,被刷卡动画到堆栈后部而不是被取出. `emptyView` 无法在此模式中设置 . |
| `onSwipe` | `(direction: SwipeDirection, cardId: string) => void` | —— 说 | 在卡牌承诺出道(动画完成前)之后,每张刷卡一次拨打. |
| `swipeStyle` | `"discard" | "sendToBack"` | `"discard"` | `"discard"` 将牌从屏幕上扔出并淡出. `"sendToBack"` 将它滑过堆叠边缘,然后在其他牌下进行动画. |
| `sendToBackMargin` | `number` | `0` | 额外的像素,卡片在被发送到后方之前会经过堆栈边缘. 仅在 `swipeStyle` 实值 `"sendToBack"`。 。 。 . |
| `getCardElement` | `(element: Element) => Element` | 第一个孩子 | 接收包装元件并返回实际的卡片元件来进行相撞检测. 默认为 `element.firstElementChild ?? element`,因此默认情况下,包装上的任何粘贴都排除在外。 当可见卡不是包装人的第一个孩子时(例如当您将额外布局元素嵌入包装人内部时)被覆盖. |
| `swipeDirections` | `AllowedSwipeDirection | AllowedSwipeDirection[]` | 全部4个 | 限制可以刷卡的方向 。 接受单个值或数组 `SwipeDirection | "x" | "y" | "horizontal" | "vertical"`。快捷键扩展至相应的对(`"x"`编号 :`"horizontal"` → `["left", "right"]`, (中文(简体) ), `"y"`编号 :`"vertical"` → `["up", "down"]`) (中文(简体) ). 约束指针拖到允许的轴/方向并禁用匹配的方向按钮. |
| `ref` | `Ref<HTMLDivElement>` | —— 说 | 转发到根 `<div>`。 。 。 . |
| `...props` | `ComponentPropsWithoutRef<"div">` | —— 说 | 所有标准 `<div>` 道具( r)`className`, (中文(简体) ), `style`, (中文(简体) ), `children`(见a/c.5/49/70,附件). |

#### 根上的数据属性集

| 属性 | 数值 | 说明 |
| --- | --- | --- |
| `data-swipeable-cards-root` | `""` | 总是在场。 标记上下文查看的根 . |

* * *

### `SwipeableCards.Cards`

堆货箱 设置您卡片样式使用的 CSS 自定义属性 计算堆叠卡的z-index,缩放,不透明等. 显示 a `<div>` 外加内脏 `<div data-empty>` 当堆栈是 空( 空视图总是渲染出, 这样您就可以在外/ 外切换 - 自由的) (英语).

| Prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `visibleStackLength` | `number` | `4` | 可见的外观中应该出现多张从堆起的卡片. 用来计算 `--visible-stack-length` 你的风格. |
| `children` | `ReactNode | (stack: CardWithId[]) => ReactNode` | 每张卡片一张 | 或预发子或接收当前堆栈并返回节点的函数 。 需要每张卡定制时使用函数表. |
| `ref` | `Ref<HTMLDivElement>` | —— 说 | 转发到卡片 `<div>`。 。 。 . |
| `...props` | `Omit<ComponentPropsWithoutRef<"div">, "children">` | —— 说 | 所有标准 `<div>` 道具除外 `children` (被儿童功能变体所压倒). |

#### 卡片容器上的数据属性集

| 属性 | 数值 | 说明 |
| --- | --- | --- |
| `data-swipeable-cards-cards` | `""` | 总是在场。 内部用来定位堆栈. |
| `data-empty` | `""` | 设置在持有空视图的内包装上 . |

* * *

### `SwipeableCards.CardWrapper`

单张牌的包装纸 拥有该卡的指针交互—— 只有顶卡回复 `pointerdown`显示 a `<div>` 包含 无论是 `card.card` 或者,如果你通过了 `children`,你自己的内容.

| Prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `card` (必须) | `CardWithId` | —— 说 | 这个 `{ id, card }` 从此包装符所代表的堆栈中输入. |
| `children` | `ReactNode` | `card.card` | 覆盖卡片自己的JSX. 用于卡片上方的分层控制,同时保留包装处理指针事件. |
| `ref` | `Ref<HTMLDivElement>` | —— 说 | 转发到包装器 `<div>`。 。 。 . |
| `...props` | `ComponentPropsWithoutRef<"div">` | —— 说 | 所有标准 `<div>` 道具来. `onPointerDown` 财务报告和审定财务报表 `onDragStart` 与内部处理者合并. |

#### 每个包装上的数据属性集

| 属性 | 数值 | 说明 |
| --- | --- | --- |
| `data-swipeable-cards-card-wrapper` | `""` | 总是在场。 用于内部列出堆放项目. |
| `data-swipeable-cards-id` | 页:1 `card.id` | 让您瞄准来自 CSS 或查询选择器的特定卡片. |
| `data-swipeable-cards-top-card` | `"true"` | `"false"` | `"true"` 当此包装目前为顶卡时。 用于用属性选择器突出显示可操作卡 . |

* * *

### `SwipeableCards.SwipeLeftButton` 编号 : `SwipeRightButton` 编号 : `SwipeUpButton` 编号 : `SwipeDownButton`

程序刷新按钮。 单击一个会触发当前顶部的擦拭 以随机键、速度和旋转为方向的卡片,所以 重复点击感觉自然。 当堆栈为空时自动取消操作或 刷卡工作已经开始。 显示 a `<button>`。 。 。 .

| Prop | 类型 | 默认 | 说明 |
| --- | --- | --- | --- |
| `onClick` | `MouseEventHandler<HTMLButtonElement>` | —— 说 | 在刷子被触发前同步调用 。 可用于分析,声音效果,或同步伴奏UI动画. |
| `ref` | `Ref<HTMLButtonElement>` | —— 说 | 已转交 `<button>`。 。 。 . |
| `...props` | `ComponentPropsWithoutRef<"button">` | —— 说 | 所有标准 `<button>` 道具来. |

* * *

## 钩子

### `SwipeableCards.useSwipeableCardsContext()`

返回完整的内部环境。 必须在里面使用 `SwipeableCards.Root`。 。 。 .

```tsx
const {
  loop,
  emptyView,
  cards,
  stack,
  setStack,
  discardedCardId,
  setDiscardedCardId,
  dragStateRef,
  animationRef,
  commitSwipe,
  swipeStyle,
  getCardElement,
  sendToBackMargin,
  rootRef,
  swipeDirections,
  swipeDirectionsRef,
} = SwipeableCards.useSwipeableCardsContext();
```

| 财产 | 类型 | 说明 |
| --- | --- | --- |
| `loop` | `boolean | undefined` | 反映 `loop` 道具传到 `Root`。 。 。 . |
| `emptyView` | `ReactNode` | 空视图传至 `Root` (仅在 `loop` 实值 `false`) (中文(简体) ). |
| `cards` | `CardWithId[]` | 原画 `cards` 道具传到 `Root`启用 `stack` {\\fn黑体\\fs22\\bord1\\shad0\\3ahbe\\4ah00\\fscx67\\fscy66\\2chffffff\\3ch808080}现场的变异名单. |
| `stack` | `CardWithId[]` | 现相堆. 牌被刷去时被取出(或旋转,回转时被取出). |
| `setStack` | `Dispatch<SetStateAction<CardWithId[]>>` | 一定要替换堆栈。 用于按需添加新牌. |
| `discardedCardId` | `string` | 卡片的ID 正在动画中,或者 `""` 如果没有在刷卡. |
| `setDiscardedCardId` | `Dispatch<SetStateAction<string>>` | 改善上述情况(需要外部援助). |
| `dragStateRef` | `RefObject<DraggingState>` | 可变拖放状态(指向位置,速度,支点,当前被拖放的元素). 拖曳时读取自定义 UI 反馈 . |
| `animationRef` | `RefObject<Animation[]>` | 目前运行的Web动画API动画为活性刷卡. |
| `commitSwipe` | `(manual?: boolean, event?: PointerEvent) => void` | 将当前拖动状态提交到刷卡中(或者如果手势太小,将卡返回到堆栈中). |
| `swipeStyle` | `"discard" | "sendToBack"` | 反映 `swipeStyle` 道具 (英语). |
| `getCardElement` | `(element: Element) => Element` | 反映 `getCardElement` 道具 (英语). |
| `sendToBackMargin` | `number` | 反映 `sendToBackMargin` 道具 (英语). |
| `rootRef` | `RefObject<HTMLDivElement>` | 对根 `<div>` 执行人: `SwipeableCards.Root`。 。 。 . |
| `swipeDirections` | `SwipeDirection[]` | 已规范的允许刷取方向列表( 默认为全部 4) 。 消费者在 `swipeDirections` 道具变化. |
| `swipeDirectionsRef` | `RefObject<SwipeDirection[]>` | 在事件处理器没有重新装订的收听器的情况下,将上面的镜像反射到任何读取的地方. |

* * *

### `SwipeableCards.useSwipeableCardsStack()`

仅返回当前状态的便捷钩 `stack` (记取. 使用这个 在只关心堆栈的组件中,以避免重接 不相关的上下文变化.

```tsx
const stack = SwipeableCards.useSwipeableCardsStack();
```

* * *

### `SwipeableCards.useProgrammaticSwipe()`

将内置方向按钮赋予权力. 用它来制造自己的刷子 触发器。 如果周围的树不包括一个 `SwipeableCards.Cards` 触发时装入容器.

```tsx
const { trigger, swipeStyle, swipeDirections } =
  SwipeableCards.useProgrammaticSwipe();
```

| 财产 | 类型 | 说明 |
| --- | --- | --- |
| `trigger` | `(configure: (state: DraggingState, rect: DOMRect) => void) => void` | 触发上卡的刷卡.  `configure` 调回调取( 零) 拖放状态和包装器的绑定矩形; 设置 `velocityX/Y`, (中文(简体) ), `pivotX/Y`, (中文(简体) ), `startX/Y`, (中文(简体) ), `lastX/Y`。 。 。 . |
| `swipeStyle` | `"discard" | "sendToBack"` | 显示活动 `swipeStyle` 因此,您的自定义按钮可以选择寻找任一模式的快速值. |
| `swipeDirections` | `SwipeDirection[]` | 已规范的允许方向列表, 用于在不允许您自定义触发时使其失效 . |

#### 示例: "超像" 向上刷

```tsx
function SuperLikeButton() {
  const { trigger } = SwipeableCards.useProgrammaticSwipe();
  return (
    <button
      type="button"
      onClick={() =>
        trigger((state, rect) => {
          state.velocityX = 0;
          state.velocityY = -(rect.height / 442) * 4;
          state.pivotX = 0;
          state.pivotY = -0.4;
          state.startY = 0;
          state.lastY = -1;
        })
      }
    >
      ⭐
    </button>
  );
}
```

* * *

## CSS 自定义属性

组件设置这些 CSS 自定义属性,以便您能够完全驱动 视觉堆栈外观来自您的 CSS, 不含道具钻取值 进入每张卡片.

#### 打开 `SwipeableCards.Cards`

| 财产 | 说明 |
| --- | --- |
| `--stack-length` | 目前堆栈中的牌数 . |
| `--visible-stack-length` | “peek”中的牌数(最上方牌后可见的上部),盖到 `visibleStackLength`。 。 。 . |

#### 每个 `SwipeableCards.CardWrapper`

| 财产 | 说明 |
| --- | --- |
| `--stack-index` | 由上而上地取出1个卡片索引. 顶卡是 `1`。 。 。 . |
| `--stack-index0` | 从上取出卡的0基指数. 顶卡是 `0`。 。 。 . |

#### 示例样式

```css
[data-swipeable-cards-card-wrapper] {
  position: absolute;
  inset: 0;
  transform-origin: center top;
  transition:
    scale 0.3s,
    translate 0.3s;
  scale: calc(1 - var(--stack-index0) * 0.05);
  translate: 0 calc(var(--stack-index0) * clamp(16px, 1vw, 32px));
  z-index: var(--stack-length);
  pointer-events: none;
}

[data-swipeable-cards-top-card="true"] {
  pointer-events: auto;
  cursor: grab;
}

[data-swipeable-cards-top-card="true"]:active {
  cursor: grabbing;
}
```

* * *

## 导出类型

```ts
import type {
  CardWithId,
  SwipeDirection,
  AllowedSwipeDirection,
  SwipeStyle,
  GetCardElement,
  StackRenderer,
  DraggingState,
  BaseSwipeableCardsProps,
  NotLoopingSwipeableProps,
  LoopingSwipeableProps,
  SwipeableCardsProps,
  SwipeableCardsCardsProps,
  SwipeableCardsCardWrapperProps,
} from "@daformat/react-swipeable-cards";
```

| 类型 | 形状 |
| --- | --- |
| `CardWithId` | `{ id: string; card: JSX.Element }` |
| `SwipeDirection` | `"left" | "right" | "up" | "down"` |
| `AllowedSwipeDirection` | `SwipeDirection | "x" | "y" | "horizontal" | "vertical"` ——被大会接受 `swipeDirections` 道具 (英语). |
| `SwipeStyle` | `"discard" | "sendToBack"` |
| `GetCardElement` | `(element: Element) => Element` |
| `StackRenderer` | `(stack: CardWithId[]) => ReactNode` |
| `DraggingState` | 可变拖动状态对象(指针位置,速度,中枢,当前被拖动的元素). 见 `dragStateRef` 见上文. |
| `SwipeableCardsProps` | 歧视工会 `NotLoopingSwipeableProps` 财务报告和审定财务报表 `LoopingSwipeableProps`。 。 。 . |

## 常见问题

### @daformat/react-swipeable-cards 是什么？

无头反应可刷卡自旋组件,类似于 Tinder, 没有依赖性

### 如何安装 @daformat/react-swipeable-cards？

在项目目录运行 npm install @daformat/react-swipeable-cards，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：react（\>=18）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @daformat/react-swipeable-cards 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @daformat/react-swipeable-cards 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @daformat/react-swipeable-cards 可以商用吗？

@daformat/react-swipeable-cards 使用 Zero-Clause BSD 许可。声明的许可为 Zero-Clause BSD，请阅读许可原文确认使用条件。以上为概括，不构成法律意见，请以许可原文为准。

### @daformat/react-swipeable-cards 的下载量有多少？

根据 npm 官方下载统计，@daformat/react-swipeable-cards 在 2026/10/11 – 2026/11/09 这 30 天内被下载 9,858 次，日均约 329 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @daformat/react-swipeable-cards 还在维护吗？

最新版本 1\.0\.4 发布于 2026/05/19（5 个月前）；至今共发布 4 个版本，其中过去 12 个月发布了 4 个；首次发布于 2026/05/03。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

