apple-liquid-tabbar
uni-app Vue3 微信小程序(Skyline)液态玻璃底部 tabBar。
原生 tabBar / 自绘底栏很难同时做到:玻璃质感、果冻切换、中间凸起、长按跟手,又兼容 Skyline worklet。本包把这套交互封装成可安装组件——默认浮在底部(含安全区),能力不够时自动降级,业务只接 v-model 与事件。
要求: Vue3、微信小程序 Skyline。MIT。
安装
npm i apple-liquid-tabbar
# 或 pnpm add / yarn add
使用
1. vite.config.ts
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import { appleLiquidTabbar } from 'apple-liquid-tabbar'
export default defineConfig({
plugins: [uni(), appleLiquidTabbar()],
})
2. 页面
<script setup lang="ts">
import { ref } from 'vue'
import AppleLiquidTabbar from 'apple-liquid-tabbar'
import type {
LiquidTabItem,
LiquidTabChangePayload,
LiquidActionPayload,
} from 'apple-liquid-tabbar'
type TabMeta = { key: string; path: string }
const current = ref(0)
const tabs: LiquidTabItem<TabMeta>[] = [
{
id: 'home',
text: '首页',
icon: '/static/tab/home.png',
activeIcon: '/static/tab/home-on.png',
meta: { key: 'home', path: '/pages/home/index' },
},
{
id: 'me',
text: '我的',
icon: '/static/tab/user.png',
activeIcon: '/static/tab/user-on.png',
meta: { key: 'me', path: '/pages/me/index' },
},
]
const onChange = (e: LiquidTabChangePayload<TabMeta>) => {
// 例:{ index: 1, prevIndex: 0, reason: 'tap', item: { id: 'me', text: '我的', meta: { key: 'me', path: '...' }, ... } }
current.value = e.index
uni.switchTab({ url: e.item.meta!.path })
}
const onAction = (e: LiquidActionPayload<TabMeta>) => {
// 例:{ activeIndex: 0, activeItem: { id: 'home', ... }, action: { id: 'add', text: '添加', ... } }
console.log('action', e.action.id, 'from tab', e.activeIndex)
}
</script>
<template>
<AppleLiquidTabbar
v-model="current"
:tabs="tabs"
:action-button="{ id: 'add', text: '添加' }"
@change="onChange"
@action="onAction"
/>
</template>
easycom:
{
"easycom": {
"custom": {
"^apple-liquid-tabbar$": "@/uni_modules/apple-liquid-tabbar/views/AppleLiquidTabbar.vue"
}
}
}
Props
| 参数 |
类型 |
默认 |
说明 |
tabs |
LiquidTabItem<T>[] |
— |
Tab 列表(不含中间钮);T 为 meta 类型 |
v-model |
number |
0 |
当前选中下标(相对 tabs) |
actionButton |
LiquidActionButtonProp<T> |
— |
中间凸起钮;取值见下 |
theme |
TabbarThemeOptions |
内置 |
主题模式与颜色 |
layout |
TabbarLayoutOptions |
内置 |
栏身尺寸与定位 |
pill |
TabbarPillOptions |
内置 |
选中胶囊几何 |
motion |
TabbarMotionOptions |
内置 |
果冻、长按跟手、按压、震动 |
quality |
TabbarQualityOptions |
内置 |
动效档位与调试日志 |
experimental |
TabbarExperimentalOptions |
内置 |
实验特性开关 |
actionButton 传什么,中间凸起钮怎么表现:
| 传入值 |
效果 |
不传 / undefined / false |
不显示中间钮 |
true |
显示默认「+」 |
对象(见下方 LiquidActionButton) |
自定义图标、文案、meta 等 |
null |
强制关闭(覆盖默认逻辑时用) |
中间钮不占用 v-model 下标,点击走 @action,不会改当前选中 Tab。
LiquidTabItem<T>
| 字段 |
类型 |
必填 |
说明 |
id |
string |
是 |
业务侧稳定唯一标识 |
text |
string |
是 |
Tab 文案 |
icon |
string |
是 |
未选中图标路径 |
activeIcon |
string |
是 |
选中图标路径 |
disabled |
boolean |
否 |
为 true 时灰显且不可切换 |
badge |
number |
否 |
角标数字;0 / 不传不显示;大于 99 显示 99+ |
meta |
T |
否 |
业务自定义数据;组件不读取,随 change 等事件原样回传 |
LiquidActionButton<T>
| 字段 |
类型 |
说明 |
id |
string |
动作标识 |
icon |
string |
圆内图标路径;不传则显示默认「+」 |
text |
string |
圆下文案,如「添加」 |
ariaLabel |
string |
无障碍朗读文案 |
disabled |
boolean |
为 true 时不可点击 |
meta |
T |
业务自定义数据;组件不读取,随 action 事件原样回传 |
事件
| 事件 |
回调参数 |
触发时机 |
update:modelValue |
index: number |
选中下标变化(与 v-model 同步) |
change |
LiquidTabChangePayload<T> |
短按切换,或长按 scrub 松手吸附后 |
action |
LiquidActionPayload<TTab, TAction> |
点击中间凸起钮(中间钮不改 v-model) |
LiquidTabChangePayload<T>
| 字段 |
类型 |
说明 |
index |
number |
新选中下标(相对 tabs,不含中间钮) |
item |
LiquidTabItem<T> |
新选中项(含 meta) |
reason |
'tap' | 'scrub' |
tap 短按;scrub 长按滑动松手 |
prevIndex |
number |
变更前下标 |
回调示例(从「首页」短按切到「我的」):
const onChange = (e: LiquidTabChangePayload<TabMeta>) => {
console.log(e)
// {
// index: 1,
// prevIndex: 0,
// reason: 'tap', // 长按滑动松手则为 'scrub'
// item: {
// id: 'me',
// text: '我的',
// icon: '/static/tab/user.png',
// activeIcon: '/static/tab/user-on.png',
// meta: { key: 'me', path: '/pages/me/index' },
// },
// }
}
LiquidActionPayload<TTab, TAction>
| 字段 |
类型 |
说明 |
action |
LiquidActionButton<TAction> |
当前中间钮配置(含 meta) |
activeIndex |
number |
点击时已选中的 Tab 下标 |
activeItem |
LiquidTabItem<TTab> | null |
点击时已选中的 Tab 项 |
回调示例(当前在「首页」,点中间「添加」):
const onAction = (e: LiquidActionPayload<TabMeta>) => {
console.log(e)
// {
// activeIndex: 0,
// activeItem: {
// id: 'home',
// text: '首页',
// icon: '/static/tab/home.png',
// activeIcon: '/static/tab/home-on.png',
// meta: { key: 'home', path: '/pages/home/index' },
// },
// action: {
// id: 'add',
// text: '添加',
// // icon / meta / ... 按你传入的配置原样带回
// },
// }
}
TabbarThemeOptions
| 字段 |
类型 |
默认 |
说明 |
mode |
LiquidThemeMode |
'auto' |
'auto' 跟随系统;'dark' / true 强制深色;'light' / false 强制浅色 |
colors |
TabbarColors |
{} |
浅色模式下的颜色覆盖 |
colorsDark |
TabbarColors |
{} |
深色生效时与 colors 合并,本对象优先 |
TabbarColors
| 字段 |
类型 |
说明 |
accent |
string |
品牌强调色(选中文案兜底、中间钮等) |
label |
string |
未选中 Tab 文案颜色 |
labelActive |
string |
选中 Tab 文案颜色;不传可回退 accent |
barFill |
string |
栏身填充色(建议带透明度) |
barStroke |
string |
栏身描边色 |
pillFill |
string |
选中胶囊填充色 |
pillStroke |
string |
选中胶囊描边色 |
badgeBg |
string |
角标背景色 |
badgeText |
string |
角标数字颜色 |
actionBg |
string |
中间钮圆形背景色 |
actionIcon |
string |
中间钮图标颜色 |
actionLabel |
string |
中间钮文案颜色 |
iconTint |
string |
未选中图标着色(仅 mask / 单色图;普通 PNG 无效) |
iconActiveTint |
string |
选中图标着色(同上) |
specular |
string |
玻璃顶缘高光叠色 |
shadow |
string |
栏身投影颜色(可含透明度) |
TabbarLayoutOptions
| 字段 |
类型 |
默认 |
说明 |
positionMode |
'fixed' | 'absolute' | 'relative' |
'fixed' |
fixed / absolute 由组件定位;relative 占文档流,由外层布局 |
barHeight |
number |
62 |
栏身高度(px) |
barRadius |
number |
31 |
栏身圆角(px);不传约等于 barHeight / 2 |
barWidthPercent |
number |
92 |
fixed / absolute 时相对屏幕宽度的百分比;relative 时无效 |
TabbarPillOptions
| 字段 |
类型 |
默认 |
说明 |
height |
number |
54 |
选中胶囊高度(px);大于 0 时竖直居中 |
insetX |
number |
内置 |
中间项左右缩进(px),胶囊比热区略窄 |
insetY |
number |
内置 |
上下缩进(px);height 为 0 时用来推算高度 |
edgeInset |
number |
内置 |
首项 / 末项贴栏身外缘的缩进(px) |
minWidth |
number |
内置 |
胶囊最小宽度(px) |
TabbarMotionOptions
| 字段 |
类型 |
默认 |
说明 |
haptic |
boolean |
false |
切换 Tab、长按 scrub 时是否震动反馈 |
jellyPreset |
JellyPresetId |
'snappy' |
果冻手感预设,见下表 |
jelly |
TabbarJellyOptions |
— |
覆盖预设的弹簧参数;不传则完全跟预设 |
settle |
TabbarSettleOptions |
— |
切 Tab 竖直过冲(上下对称) |
scrub |
TabbarScrubOptions |
— |
长按左右滑动时胶囊形变 |
press |
TabbarPressOptions |
— |
按下时整栏 / 单项缩放 |
JellyPresetId
| 值 |
说明 |
snappy |
跟手、偏快,果冻行程短(默认) |
appstore |
偏黏、拖尾更明显、回弹少 |
extra |
拉伸更夸张,适合演示 |
subtle |
克制、接近系统感 |
TabbarSettleOptions
| 字段 |
类型 |
默认 |
说明 |
heightBoost |
number |
0.22 |
切 Tab 时相对高度膨胀比例;上下各一半溢出 |
squash |
number |
0.28 |
横向拉宽时高度压扁系数 |
stiffness |
number |
140 |
竖直回落弹簧刚度 |
damping |
number |
10 |
竖直回落阻尼;越小越弹 |
TabbarJellyOptions
| 字段 |
类型 |
说明 |
leadStiffness |
number |
领先弹簧刚度;越大跟手越紧、到位越快 |
leadDamping |
number |
领先弹簧阻尼;越大越少抖动 |
lagStiffness |
number |
滞后弹簧刚度;控制拖尾「慢半拍」的硬度 |
lagDamping |
number |
滞后弹簧阻尼;控制拖尾衰减快慢 |
TabbarScrubOptions
| 字段 |
类型 |
默认 |
说明 |
widthBoost |
number |
1.12 |
长按滑动时胶囊相对常态的横向拉宽倍率 |
midExtra |
number |
0.32 |
滑到两个 Tab 正中间时额外加宽量(相对宽度比例) |
heightExtra |
number |
15 |
长按高度外溢(px),上下各一半 |
topLift |
number |
0 |
额外整体上浮;默认 0 保持上下对称 |
TabbarPressOptions
| 字段 |
类型 |
默认 |
说明 |
barScale |
number |
0.97 |
按下时整栏缩放比例(小于 1 为缩小) |
itemScale |
number |
1.18 |
按下时当前热区单项缩放比例(大于 1 为放大) |
TabbarQualityOptions
| 字段 |
类型 |
默认 |
说明 |
mode |
TabbarMode |
'auto' |
动效实现:auto 自动降级;worklet Skyline 果冻;spring JS 弹簧;static 无果冻 |
reduceMotion |
boolean |
false |
true 时关闭果冻,切换接近瞬时 |
styleDegrade |
boolean |
false |
true 时关闭模糊与玻璃妆面 |
debug |
boolean | 'auto' |
'auto' |
控制台 runtime 日志;'auto' 开发/体验开、正式关;true / false 强制 |
TabbarExperimentalOptions
| 字段 |
类型 |
默认 |
说明 |
darkBlend |
boolean |
false |
深色模式下额外混合处理 |
iconCrossfade |
boolean |
false |
选中切换时图标交叉淡入 |
插件参数
appleLiquidTabbar(options?)
| 字段 |
类型 |
默认 |
说明 |
mount |
string |
src/uni_modules/apple-liquid-tabbar |
同步与解析用的本地目录(相对项目根) |
mountAlias |
string |
@/uni_modules/apple-liquid-tabbar |
改写后的 import 路径前缀 |
root |
string |
Vite root / cwd |
解析 mount 时的项目根目录 |