更新记录
1.0.0(2026-09-03) 下载此版本
翻转卡片组件
平台兼容性
uni-app
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | √ | √ | √ | √ |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ | √ |
其他
| 多语言 | 暗黑模式 | 宽屏模式 |
|---|---|---|
| √ | √ | √ |
SL-WX-FlipCard 翻转卡片组件
组件简介
基于 CSS Transform 体系实现的点击翻转卡片组件。内置 7 种切换动画(左右翻转、上下翻转、旋风旋转、淡入淡出、底部滑入、右侧滑入、缩放切换),支持单列 / 多列网格布局、互斥展开(一次只翻一张)、正/背面插槽自定义、批量翻转与还原的对外 API。H5 / 微信小程序通用。
修复说明:在部分 WebView / 小程序环境下
backface-visibility: hidden仅视觉透明,仍会拦截二次点击。组件已通过opacity + visibility + pointer-events三重控制 + 动画半程切换策略彻底解决「翻转后点不回来」的问题,7 种动画全部通过同一可见性机制保障交互一致性。
目录结构
components/SL-WX-FlipCard
└── SL-WX-FlipCard.vue
核心特性
-
7 种内置动画:通过
animation属性切换,涵盖 3D 翻牌类(flipY / flipX)、2D 趣味类(rotate)、轻量类(fade)、滑动类(slideUp / slideLeft)、透视缩放类(zoom)共 7 种风格。3D 类动画自动启用perspective + preserve-3d,2D 类动画自动关闭 3D 上下文以减少合成层开销。 -
单列 / 多列自由切换:
columnCount设置列数(1\~N),内部使用 CSS Grid 自适应排列,列间距、行间距、外层内边距均可独立配置。 -
点击翻转(全端统一):点击任意卡片在正/背面之间切换,H5 与小程序行为完全一致;所有动画均保证"翻转后二次点击必定翻回"。
-
互斥展开:
exclusive = true时,一次只能有一张卡片处于背面,适合「问答」「翻牌游戏」「单选卡片」等场景。 -
任意宽高比:通过
aspectRatio(如1 / 1、16 / 10、1 / 1.2)控制卡片形状。 -
正/背面内容完全自定义:默认根据数据 key 自动渲染「图片 + 底部渐变标题/描述」,也可通过
#front/#back具名插槽完全重写。 -
无图模式自动适配:未提供图片字段时,正面白底深色字,背面浅灰底蓝字,居中对齐显示纯文字。
-
对外方法:暴露
flip(index, flipped)和reset(),通过$refs调用。
动画类型一览(animation prop)
| 动画值 | 名称 | 类型 | 效果说明 | 适用场景 |
|---|---|---|---|---|
flipY |
左右翻转 | 3D | 沿垂直中线做 Y 轴 180° 翻转(经典扑克牌翻牌),默认动画 | 商品卡片 / 相册 / 记忆翻牌游戏 |
flipX |
上下翻转 | 3D | 沿水平中线做 X 轴 180° 翻转 | 答题卡 / 门票/票据 |
rotate |
旋风旋转 | 2D | 绕中心旋转 180° 并搭配弹性缩放(cubic-bezier 回弹),正背面文字正向显示 | 抽奖 / 转盘 / 活泼的运营位 |
fade |
淡入淡出 | 2D | 正面淡出并放大至 1.1 倍,背面从 0.9 倍淡入至 1.0 倍 | 相册 / 资讯 / 视觉温和的场景 |
slideUp |
底部滑入 | 2D | 背面从卡片底部 100% 处滑入,正面同步向上滑出 | 列表项详情 / 问答 / 提示卡 |
slideLeft |
右侧滑入 | 2D | 背面从卡片右侧 100% 处滑入,正面同步向左滑出 | 横向分页式卡片 / 翻页式展示 |
zoom |
缩放切换 | 2D | 正面缩小至 0.2× 消失,背面从 0.2× 放大到 1.0× 出现(分阶段 opacity 平滑过渡) | 照片集 / 作品展示 / 主题切换卡片 |
小提示:同一页面多次切换
animation时,建议先调用reset()清空所有卡片的翻转态,避免处于"半翻转"中间态时动画算法不同导致的错位感。
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
list |
Array | [] |
数据列表 |
columnCount |
Number | 2 |
列数,1 为单列,2+ 为多列 |
animation |
String | 'flipY' |
动画类型:flipY / flipX / rotate / fade / slideUp / slideLeft / zoom |
aspectRatio |
String | '1 / 1' |
卡片宽高比(width / height),如 '16 / 10'、'1 / 1.2' |
duration |
Number | 600 |
动画时长(ms),可见性切换点自动取 duration/2 |
radius |
Number | 16 |
卡片圆角(rpx) |
columnGap |
Number | 20 |
列间距(rpx) |
rowGap |
Number | 20 |
行间距(rpx) |
padding |
Number | 24 |
外层容器内边距(rpx) |
exclusive |
Boolean | false |
是否互斥展开,true 时一次只能翻转一张 |
frontImageKey |
String | 'frontImage' |
正面图片字段名 |
frontTitleKey |
String | 'frontTitle' |
正面标题字段名 |
frontDescKey |
String | 'frontDesc' |
正面描述字段名 |
backImageKey |
String | 'backImage' |
背面图片字段名 |
backTitleKey |
String | 'backTitle' |
背面标题字段名 |
backDescKey |
String | 'backDesc' |
背面描述字段名 |
Events
| 事件名 | 回调参数 | 说明 |
|---|---|---|
itemtap |
{ item, index, flipped } |
点击卡片时触发,返回当前项数据、索引和翻转后的状态 |
onItemTap({ item, index, flipped }) {
console.log('第' + index + '张,翻转后:' + flipped, item)
}
Slots
| 插槽名 | 作用域参数 | 说明 |
|---|---|---|
front |
{ item, index } |
自定义卡片正面内容(覆盖默认的「图片+标题+描述」渲染) |
back |
{ item, index } |
自定义卡片背面内容(覆盖默认的「图片+标题+描述」渲染) |
对外方法
<sl-wx-flip-card ref="flip" :list="list" animation="zoom" />
| 方法名 | 参数 | 说明 |
|---|---|---|
flip |
(index?, flipped?) |
手动翻转。传 index 翻转指定卡片;不传(或传 null)则作用于全部;flipped = true/false 强制指定状态,不传则切换 |
reset |
- | 将所有卡片还原到正面 |
this.$refs.flip.flip(2, true) // 第 3 张翻到背面
this.$refs.flip.flip(0) // 切换第 0 张
this.$refs.flip.flip(null, true) // 全部翻开
this.$refs.flip.reset() // 全部还原
使用示例
示例 1:2 列图片卡片 + 缩放动画(zoom)
<template>
<sl-wx-flip-card
ref="flip"
:list="list"
:column-count="2"
animation="zoom"
:aspect-ratio="'1 / 1'"
:duration="550"
:column-gap="16"
:row-gap="16"
@itemtap="onTap"
/>
</template>
<script>
import SlWxFlipCard from '@/components/SL-WX-FlipCard/SL-WX-FlipCard.vue'
export default {
components: { SlWxFlipCard },
data() {
return {
list: [
{
frontImage: 'https://picsum.photos/seed/a/600/600',
frontTitle: '雪山之境',
frontDesc: '点击缩放查看',
backImage: 'https://picsum.photos/seed/ab/600/600',
backTitle: '喜马拉雅',
backDesc: '海拔 8848 米,常年积雪。'
},
{
frontImage: 'https://picsum.photos/seed/b/600/600',
frontTitle: '城市夜景',
frontDesc: '霓虹闪烁',
backImage: 'https://picsum.photos/seed/bb/600/600',
backTitle: '上海外滩',
backDesc: '万国建筑博览群。'
}
]
}
},
methods: {
onTap({ item, index, flipped }) {
console.log(item, index, flipped)
}
}
}
</script>
示例 2:问答卡片 + 旋风动画(rotate) + 互斥
<template>
<sl-wx-flip-card
:list="qaList"
:column-count="3"
animation="rotate"
:aspect-ratio="'1 / 1.2'"
:exclusive="true"
front-title-key="q"
back-title-key="aTitle"
back-desc-key="a"
/>
</template>
<script>
export default {
data() {
return {
qaList: [
{ q: '1+1=?', aTitle: '答案:2', a: '十进制加法的基础结果。' },
{ q: '地球是圆的吗?', aTitle: '答案:是', a: '两极稍扁、赤道略鼓的椭球体。' },
{ q: '水的化学式?', aTitle: '答案:H₂O', a: '2 个氢原子 + 1 个氧原子。' }
]
}
}
}
</script>
示例 3:横向滑入动画(slideLeft) + 单列大卡片
<template>
<sl-wx-flip-card
:list="list"
:column-count="1"
animation="slideLeft"
:aspect-ratio="'16 / 10'"
:radius="20"
:duration="500"
/>
</template>
示例 4:淡入淡出 + 插槽自定义内容
<template>
<sl-wx-flip-card
:list="list"
:column-count="2"
animation="fade"
:duration="500"
>
<template #front="{ item }">
<view class="front">
<image :src="item.cover" mode="aspectFill" class="front__img" />
<view class="front__tag">{{ item.tag }}</view>
</view>
</template>
<template #back="{ item }">
<view class="back">
<text class="back__price">¥ {{ item.price }}</text>
<text class="back__desc">{{ item.detail }}</text>
<button size="mini" type="primary">立即购买</button>
</view>
</template>
</sl-wx-flip-card>
</template>
示例 5:外部控制 + 上下翻转(flipX)
<template>
<view>
<view class="btns">
<button @tap="$refs.flip.flip(null, true)">全部翻开</button>
<button @tap="$refs.flip.flip(null, false)">全部正面</button>
<button @tap="$refs.flip.reset()">全部还原</button>
</view>
<sl-wx-flip-card
ref="flip"
:list="list"
:column-count="3"
animation="flipX"
/>
</view>
</template>
动画实现原理简述
所有 7 种动画共用同一套可见性切换机制,保证交互统一且不会出现"点不回来":
- 可见性切换时机:
visibility与pointer-events使用 0s duration 的 transition,延迟到duration/2执行——即在动画半程(卡片垂直于屏幕 / 两面都不可见的瞬间)完成事件命中面的切换。 - 正反双向延迟方向不同:未翻转 → 翻转过程中,前半程点击仍命中正面、后半程命中背面;反向同理,避免刚点下一瞬间被另一张面抢走事件。
- 3D 与 2D 的区分:外层
.sl-flip-cell--3d(flipY / flipX)才加perspective: 1200px,.sl-flip-cell--2d(其余 5 种)关闭,减少小程序 WebView 不必要的 GPU 合成层开销。
注意事项
-
3D 兼容性:
perspective、transform-style: preserve-3d、backface-visibility已加-webkit-前缀。若低端 Android 机型在flipY / flipX下偶现闪烁,可缩短duration至400~500ms,或改用 2D 动画(rotate / fade / slideX / zoom)。 -
切换动画时请 reset:运行中从一种动画切到另一种时(例如演示页的切换条),建议先调用
$refs.flip.reset()把所有卡还原,避免处于翻转态的卡片因两套变换算法差异产生视觉"跳变"。 -
二次点击必响应:7 种动画均通过同一套
opacity + visibility + pointer-events在半程(duration/2)切换命中面,保证正面 → 背面 → 再正面的完整循环链路畅通。 -
字段 key 映射:默认渲染依赖 6 个字段;若业务字段名不同,用
frontImageKey等重映射或直接用#front / #back插槽自定义。 -
列数切换:动态改
columnCount不会清已翻状态。若要切列数时一起重置,watch columnCount后调$refs.flip.reset()。 -
互斥 + 批量 flip:
exclusive = true时flip(null, true)只会让最后一张翻转(语义约束)。若要全翻开,先把exclusive置false。

收藏人数:
下载插件并导入HBuilderX
下载示例项目ZIP
赞赏(0)
下载 104
赞赏 0
下载 12559264
赞赏 1948
赞赏
京公网安备:11010802035340号