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调用。
动画类型一览(animationprop)
| 动画值 | 名称 | 类型 | 效果说明 | 适用场景 |
|---|---|---|---|---|
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。