更新记录
3.0.5(2024-08-31) 下载此版本
- 组件bug修复
- 对案例实现底部安全区适配
3.0.4(2024-08-18) 下载此版本
- 【修复】在app上动态添加tab时,由于组件执行顺序的问题会导致内部逻辑错误
- 【修复】在快手小程序端,具名插槽包含'-'则不会渲染插槽内容,因此将nav-left、nav-right改为navLeft、navRight
- 【修复】在快手小程序端,因使用uni.createSelectorQuery().in(this).selectViewport()导致获取的显示区域的scrollTop为0造成的bug
- 【优化】组件之前对于标签栏吸顶采用的是js方案,目前已调整为:通过多种方式判断当前运行环境是否支持css sticky,如果支持则使用CSS方案,否则使用降级的JS方案
- 【新增】y-tabs新增title-active-style、title-inactive-style属性,用于设置标题项在选中和默认时的样式
- 【新增】y-tabs新增close-css-sticky属性。由于css sticky的特殊性,特提供该属性关闭css sticky方案对吸顶的实现,使用降级的js方案。
3.0.3(2024-08-08) 下载此版本
- 【修复】左右滑动超出屏幕边界时底部条错位
- 【修复】使用uni.pageScrollTo进行页面滚动时transparent不生效
平台兼容性
Vue2 | Vue3 |
---|---|
√ | √ |
App | 快应用 | 微信小程序 | 支付宝小程序 | 百度小程序 | 字节小程序 | QQ小程序 |
---|---|---|---|---|---|---|
HBuilderX 3.5.5 app-vue | × | √ | √ | √ | √ | √ |
钉钉小程序 | 快手小程序 | 飞书小程序 | 京东小程序 |
---|---|---|---|
√ | √ | √ | √ |
H5-Safari | Android Browser | 微信浏览器(Android) | QQ浏览器(Android) | Chrome | IE | Edge | Firefox | PC-Safari |
---|---|---|---|---|---|---|---|---|
√ | √ | √ | √ | √ | × | √ | √ | √ |
y-tabs 标签页组件
注意:
- 2.0.1+为非兼容性升级,重命名了诸多样式以及提供的属性名,也移除了部分属性及事件,如无必要,请勿升级,具体调整内容请查看更新记录。
- Tips
- 支持的平台
- 使用方式
- 滚动吸顶示例
- 与swiper联动示例
- Tabs Props
- Tab Props
- Tabs Events
- Tabs Methods
- Tabs Slots
- Tab Slots
- 注意事项及常见问题
Tips
- 如果有问题,可尝试下载最新代码(示例项目中的
uni_modules/y-tabs
是最新的)。 - 请保证HBuilderX正式版为
v3.4.18
、Alpha版为v3.5.2
。 - 使用该插件需安装
scss
插件。 - 点击右上角的“使用HubilderX导入示例项目”按钮下载示例项目运行并查看效果,项目中内置不少案例。
- 也可扫描右侧图片中的微信小程序码查看(由于微信小程序审核较严,无法发布新版本,因此案例较老,最好运行示例项目查看)。
- 可以群交流反馈:
;也可以邮箱或QQ留言:`@163.com`、
。
支持的平台
- H5、app-vue、微信、支付宝、钉钉、百度、字节、QQ、飞书、快手、京东小程序可用。
- 暂不支持nvue及快应用。
使用方式
1、通过HbuilerX创建项目
- 该组件符合
uni_modules
规范,使用HbuilderX
导入插件到项目根目录下的uni_modules
文件夹中。 template
中直接使用,无需单独引入注册组件。- 该组件依赖于
uni-icons
,请单独导入。
2、通过vue cli创建项目
- 需保证src下面有
uni_modules
文件夹,将y-tabs拷贝到里面 - 在
pages.json
中通过easycom
的方式引入组件(tabs组件中使用了uni-icons,因此需要引入uni-ui){ "easycom": { "autoscan": true, "custom": { "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue", "^y-(.*)": "@/uni_modules/y-$1/components/y-$1.vue" } }, }
普通案例
- 注意:在QQ/百度/字节跳动/飞书/快手/京东小程序中,自定义组件在渲染时会比App/H5端多一级节点,导致标签内容样式失效,需在组件上添加"
.y-tab-virtual
"的样式
<template>
<view>
<y-tabs v-model="activeIndex" @click="tabClick" @change="tabChange">
<y-tab class="y-tab-virtual" v-for="tab in tabs" :key="tab.title" :title="tab.title">
<view class="content-wrap"> 内容 {{tab.title}} </view>
</y-tab>
</y-tabs>
</view>
</template>
<script>
export default {
data() {
return {
activeIndex: 0,
tabs:Array.from({ length: 5 }, (o, i) => ({title: '标签' + (i + 1)})),
}
},
methods: {
// 标签点击事件
tabClick(index, item) {
console.log("tabClick", index, item);
},
// 标签切换事件
tabChange(index, item) {
console.log("tabChange", index, item);
}
}
}
</script>
<style scoped>
/* #ifdef MP-QQ || MP-BAIDU || MP-TOUTIAO || MP-LARK || MP-KUAISHOU || MP-JD */
.y-tab-virtual {
position: relative;
flex-shrink: 0;
width: 100%;
}
</style>
滚动吸顶示例
直接开启sticky
属性即可
<template>
<view>
<y-tabs v-model="activeIndex" sticky :offsetTop="offsetTop">
<y-tab class="y-tab-virtual" v-for="tab in tabs" :key="tab.title" :title="tab.title">
<view class="content-wrap"> 内容 {{tab.title}} </view>
</y-tab>
</y-tabs>
</view>
</template>
<script>
export default {
data() {
return {
activeIndex: 0,
tabs:Array.from({ length: 5 }, (o, i) => ({title: '标签' + (i + 1)})),
offsetTop: 0, //粘性布局下与顶部的最小距离
}
},
created() {
// H5端需要减去顶部导航栏高度
// #ifdef H5
this.offsetTop = 43
// #endif
},
}
</script>
<style scoped>
/* #ifdef MP-QQ || MP-BAIDU || MP-TOUTIAO || MP-LARK || MP-KUAISHOU || MP-JD */
.y-tab-virtual {
position: relative;
flex-shrink: 0;
width: 100%;
}
.content-wrap{
height:200vh;
}
</style>
与Swiper
联动示例
- 请注意
Swiper
组件的@transition
、@animationfinish
的支持平台 barAnimateMode="worm"
,设置底部条切换类似毛毛虫蠕动的效果- 经过测试,仅支持App、H5、微信、支付宝、字节跳动、飞书、QQ、快手小程序
<template>
<view>
<y-tabs ref="tabs" v-model="activeIndex" barAnimateMode="worm">
<y-tab v-for="tab in tabs" :title="tab.title" :key="tab.title" />
</y-tabs>
<swiper class="swiper" :current="activeIndex" @transition="onTransition" @animationfinish="onAnimationfinish">
<swiper-item v-for="tab in tabs" :key="tab.title">
<view class="swiper-item-view" :style="{backgroundColor: tab.color}">
{{tab.title}}
</view>
</swiper-item>
</swiper>
</view>
</template>
<script>
export default {
data() {
return {
tabs: [],
activeIndex: 0,
}
},
created() {
this.tabs = Array.from({ length: 5 }, (o, i) => {
return {
title: 'tab' + (i + 1),
color: this._getRandomColor()
}
});
},
methods: {
//swiper滑动中
onTransition(e) {
this.$refs.tabs.setDx(e.detail.dx);
},
//swiper滑动结束
onAnimationfinish(e) {
this.activeIndex = e.detail.current;
setTimeout(() => this.$refs.tabs.unlockDx(), 0) //通知y-tabs解除对setDx()的锁定
},
// 生成随机颜色
_getRandomColor() {
const rgb = [];
for (let i = 0; i < 3; ++i) {
let color = Math.floor(Math.random() * 256).toString(16)
color = color.length == 1 ? '0' + color : color
rgb.push(color)
}
return '#' + rgb.join('');
},
},
}
</script>
<style scoped>
.swiper {
height: 300rpx;
}
.swiper-item-view {
background-color: #007AFF;
height: 300rpx;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 50rpx;
}
</style>
Tabs Props
参数 | 类型 | 描述 | 默认值 | 说明 |
---|---|---|---|---|
v-model | number、string | 绑定当前选中标签的标识符 | 0 | 即tab选中项的下标或者tab的name属性值 |
type | string | 样式风格类型 | line | 可选值为 text、card、button、line-button |
color | string | 标签主题色 | #0022AB | |
background | string | 标签栏背景色 | #fff | |
title-active-color | string | 标题选中态颜色 | - | |
title-inactive-color | string | 标题默认态颜色 | - | |
title-active-style | object | 标题项选中样式 | - | 优先级 :titleStyle > titleActiveSty le > titleActiveColor |
title-inactive-style | object | 标题项默认样式 | - | 优先级:titleStyle > titleInactiveStyle > titleInactiveColor |
wrap-style | object | 标签栏样式 案例 :透明导航栏下的滚动吸顶 | - | |
direction | string | 标签栏的展示方位 | horizontal | 可选值:vertical |
duration | number、string | 动画时间,单位秒 | 0.3 | 仅支持type为line、button、line-button的滑块移动的动画时间,标签内容切换时的转场动画时间、页面级滚动导航的内容定位动画时间。 |
shrink | boolean | 通过 shrink 属性可以开启收缩布局,开启后,所有的标签会向左侧收缩对齐 | false | |
bar-width V2.0.1 | number、string | 滑块宽度,支持rpx、vh、vw等单位及calc()函数,也可传入'auto'值,默认px。 | type为line,标签栏水平、垂直时宽度分别为20px、3px,其余为选中标签宽度;为auto时,代表滑块宽度自适应于选中标签宽度(仅在type='line'且bar-animate-mode='linear'、direction='horizontal'下生效) | 仅支持type为line、button、line-button。 |
bar-height V2.0.1 | number、string | 滑块高度,支持度同bar-width | type为line,标签栏水平、垂直时宽度分别为3px、20px,其余为选中标签高度;为auto时,代表滑块高度自适应于选中标签高度(仅在type='line'、bar-animate-mode='linear'、direction='vertical'下生效) | 同bar-width |
bar-style V2.0.1 | object | 滑块样式 | - | 同bar-width |
bar-animate-mode V2.0.1 | string | 滑动切换tab内容时滑块的动画模式,默认为linear。可选值:worm(毛毛虫蠕动)、worm-ease(毛毛虫缓动效果)、none(不设置)。 | linear | 仅在 type='line'、direction='horizontal' 时有效。 可结合swiper组件使用,效果更好 案例 :新闻列表、与swiper组件联动 |
is-dynamic V2.0.1 | boolean | 标签切换后宽高是否有变化 | true | 当选中标签会放大文字、减少内间距时,开启该属性可避免滑块错位。(如果选中的标签文字使用‘font-size:20px;transition: all .2s’进行过渡变化,开启该属性仍旧会错位) |
ellipsis | boolean | 是否省略过长的标题文字 | true | 标签栏水平展示时,如果标签数量未超过滚动阈值则生效,垂直则不限制。 |
scroll-threshold | number、string | 滚动阈值,标签数量超过阈值且总宽度超过标签栏宽度时开始横向滚动 | 5 | |
scroll-to-center | boolean | 标签栏滚动时当前标签居中 | true | |
is-lazy-render | boolean | 是否开启延迟渲染(首次切换到标签时才触发内容渲染) | false | |
animated | boolean | 是否开启动画 | true | 用于开启标签栏滚动的过渡动画、切换标签内容时的转场动画、滚动导航下的内容定位动画 |
active-last V2.0.1 | boolean | 在滚动导航模式下,滚动到最后一个标签内容但其顶部未超过可视区域时,是否激活对应的标签项 | false | |
before-change | (name) => boolean | Promise | 切换标签前的回调函数,返回 false 可阻止切换,支持返回 Promise | - | name为v-model绑定的值 |
内容手势滑动切换相关属性 : | ||||
swipeable | boolean | 是否开启手势滑动切换 案例 :滑动切换 | 新闻列表 | 滚动吸顶+左右滑动 | false | |
swipe-animated | boolean | 是否开启标签内容滑动时的拖动动画 | true | swipeable为true、且is-lazy-render为false时有效 |
swipe-threshold | number、string | 滑动切换的滑动距离阈值,单位为px;表示开启手势滑动时,横向滑动多少px切换标签内容 | 120 | 快速滑动时不受限制 |
滚动吸顶相关属性 : | ||||
sticky | boolean | 是否使用粘性布局进行滚动吸顶 案例 :滚动吸顶 | false | |
offset-top | number、string | 粘性布局下标签栏与顶部的最小距离,单位为 px | 0[h5端由于导航栏为自定义导航栏,默认44] | 注意单位问题,如果是rpx,需要uni.upx2px转为px |
z-index | number、string | 粘性布局下,标签栏的z-index值 | 99 | |
sticky-threshold | number、string | 粘性布局吸顶的判断阈值 | 0 | 表示在页面滚动时,标签栏距屏幕顶部多少px时会触发吸顶函数进行吸顶判断 案例 :透明导航栏下的滚动吸顶 |
close-css-sticky | number、string | 关闭css sticky实现的标题栏吸顶方案 | false | 表示在页面滚动时,标签栏距屏幕顶部多少px时会触发吸顶函数进行吸顶判断 是否关闭使用css sticky实现标题栏吸顶,降级使用js方案实现 |
transparent | boolean | 页面滚动过程中,标题栏背景色是否透明渐变 案例 :透明渐变标题栏 | false | background属性值必须为rgba格式 |
transparent-offset | number、string | 标题栏背景色透明渐变的滚动距离 | 150 | 请保证标签内容的高度大于该值 |
滚动导航及侧边栏导航相关属性 : | ||||
scrollspy | boolean | 是否开启滚动导航;该模式下,内容将会平铺展示 案例 :滚动导航(页面级滚动) | false | 如果标签栏垂直展示,且内容平铺展示,就为侧边栏模式案例 :侧边栏导航(页面级滚动)-仿奶茶点单 |
page-scroll | boolean | 滚动导航模式下,内容区域是否跟随页面滚动 | true | 为false时,内容区域是放在scroll-view中实现的局部滚动,需自行设置标签页容器高度 案例 :侧边栏导航(区域滚动)-仿奶茶点单 |
Tab Props
参数 | 类型 | 描述 | 默认值 |
---|---|---|---|
name | number、string | 标签名称,作为匹配的标识符 | 标签的索引值 |
title | string | 标题 | - |
disabled | boolean | 是否禁用标签 | false |
dot | boolean | 是否在标题右上角显示小红点(优先级高于badge) | false |
badge | number、string | 图标右上角徽标的内容 | - |
badge-max-count V2.0.1 | number、string | 徽标数最大数字限制,超过这个数字将变成badgeMaxCount+,如果传空字符串则不设置 | 99 |
title-style | object | 自定义标题样式 | - |
title-class | string | 自定义标题类名 | - |
icon-type | string | 图标图案,为uniapp扩展组件(uni-ui)下的uni-icons的type值,customPrefix用法等同 | - |
icon-size | number、string | 图标大小 | 16 |
custom-prefix | string | 自定义图标 | - |
image-src | string | 图片路径 | - |
image-mode | string | 图片裁剪、缩放的模式,为uniapp内置组件->媒体组件—>image下的mode属性的可选值 | - |
position | string | 在有图标或图片的情况下,标题围绕它们所在的位置,可选值:left、top、bottom | right |
title-slot | boolean | 是否开启标题插槽(仅针对vue3版本的小程序端生效),为true则可以通过插槽自定义tab的标题 | false |
title-slot-name | string | 标题插槽的name值,可以设置该属性覆盖默认的标题插槽名称 | 默认为'title'+y-tab的下标(比如第一个y-tab,它的默认插槽名称就是'title0') |
Tabs Events
事件名 | 说明 | 回调参数 |
---|---|---|
click | 点击标签时触发 | name:标识符,title:标题 |
change | 当前激活的标签改变时触发 | name:标识符,title:标题 |
disabled | 点击被禁用的标签时触发 | name:标识符,title:标题 |
rendered | 标签内容首次渲染时触发(仅在开启延迟渲染后触发) | name:标识符,title:标题 |
sticky-change V2.0.1 | 吸顶时触发,仅在 sticky 模式下生效 | { isFixed: 是否吸顶 } |
loaded V2.0.6 | 组件内部初始化完成后调用 | - |
slide-change V2.0.9 | 内容页滑动时触发(仅barAnimateMode为linear、worm、worm-ease时有效) | { dx:滑动距离; rate:当前滑动长度占滑动区域的比例;targetIndex:目标下标;} |
slide-end V2.1.4 | 内容页滑动结束时触发(仅barAnimateMode为linear、worm、worm-ease时有效) | { targetIndex: 目标下标 } |
Tabs Slots
名称 | 说明 |
---|---|
navLeft V3.0.4更改 | 标题左侧内容 | 在快手小程序端,具名插槽包含'-'则不会渲染插槽内容,因此将nav-left、nav-right改为navLeft、navRight
navRight V3.0.4更改 | 标题右侧内容 |
title+下标 | 标签标题,插槽名默认为"title"+tab下标,如果用户在y-tab上设置了titleSlotName,则插槽名为titleSlotName的值(注意:vue3中仅H5、app-vue有效,小程序端需在y-tab上设置titleSlot属性才会生效) |
bar V2.0.1 | 滑块,可自定义滑块的内容(可以设置图片等) |
Tab Slots
名称 | 说明 |
---|---|
default | 标签页内容 |
Tabs Methods
通过 ref 可以获取到 Tabs 实例并调用实例方法(如果是vue3的组合式API,请使用vue3的写法);
方法名 | 说明 | 参数 | 返回值 |
---|---|---|---|
resize | 外层元素大小、标签数量变化、滑块错位以及标签定位不准时,可以调用此方法来触发组件内部初始化进行调整 | callback: 回调函数 | - |
reset | 重置组件的一些状态,并调用了resize方法进行数据初始化 | callback: 回调函数 | - |
scrollTo | 滚动到指定的标签页,在滚动导航模式下可用 | name: 标识符 | - |
setDx | 设置滑块的水平偏移量(结合swiper组件的@transition事件,设置滑块的位置;经过测试,仅在App、H5、微信、支付宝、字节跳动、QQ小程序上有良好的效果) | dx: swiper组件的e.detail.dx | - |
unlockDx | 解除对setDx()的锁定(结合swiper组件的@animationfinish事件,在动画完成后不锁定setDx) | - | - |
注意事项及常见问题
组件从隐藏状态切换到显示状态时,底部条位置错误?
Tabs 组件在挂载时,会获取自身的宽度,并计算出底部条的位置。如果组件一开始处于隐藏状态,则获取到的宽度永远为 0,因此无法展示底部条位置。
解决方法
方法一,如果是使用 v-show
来控制组件展示的,则替换为 v-if
即可解决此问题:
<!-- Before -->
<y-tabs v-show="show" />
<!-- After -->
<y-tabs v-if="show" />
方法二,调用组件的 resize 方法来主动触发重绘:
<y-tabs v-show="show" ref="tabs" />
this.$refs.tabs.resize();
在3.0.3及之前的版本,标题栏吸顶通过js方案实现,后改为了css sticky方案,如果当前运行环境不支持该css属性,才降级使用js方案。
由于css sticky的特殊性,我们不确定该属性是否受到css样式和布局的影响,可以通过closeCssSticky属性关闭css方案,使用js方案实现吸顶效果
position:sticky 的生效规则
- 必须指定 top, right, bottom 或 left 四个阈值其中之一(且达到设定的阈值),才可使粘性定位生效。否则其行为与相对定位相同;并且 top 和 bottom 同时设置时,top 生效的优先级高,left 和 right 同时设置时,left 的优先级高
- 设定为 position: sticky 的元素的任意父节点的 overflow 属性必须是 visible,否则 position:sticky 不会生效;
- 在满足上述情况下,设定了 position: sticky 的元素的父容器的高度必须大于当前元素,否则也会失效。(当然,此时,sticky 吸附的基准元素就会变成父元素)
标签选中后,样式变化可能会导致底部条错误
对于选中的标签,在放大字体的同时添加了过渡样式且有过渡时长,会使标签宽高发生变化,导致内部对于滑块的位置计算错误
解决方法
- 可以考虑给每一个标签通过
title-style
设定固定宽高,避免标签文字放大后标签宽高变化 - 如果没有过渡效果,设置
is-dynamic
即可解决滑块错误的问题,否则有过渡时长使用了该属性也难以保证滑块位置计算正确
如何解决上下滑动与左右滑动相冲突?
- 直接给标签内容具体的高度,使用
scroll-view
实现局部滚动 案例 :滑动切换 - 但是该方式会导致页面级的上拉加载与下拉刷新事件无法触发,因此需要去插件市场找类似的插件
切换标签时页面会闪烁?
标签切换后,显示的内容是异步请求获取数据进行渲染的,请给标签内容一个实际的最小高度,避免数据清空后高度回弹有导致页面闪烁
在u-popup组件中的y-tabs组件需resize一次
对于在u-popup
中的tabs
,在弹出层显示后需调用resize
方法,避免内部计算错误 案例 :在Popup中的标签页
<u-popup :show="show" :round="10" @close="show = false" @open="open">
<y-tabs ref="tabs" v-model="activeIndex">
...
</y-tabs>
</u-popup>
export default{
data(){
return {
show: false
}
},
methods:{
open(){
this.show = true;
setTimeout(()=> this.$refs.tabs.resize(),1000)
}
}
}
uni-data-select组件显示下拉选项时会被遮挡?
受限于tabs组件样式,内容区域使用了overflow:hidden
,导致uni-data-select
下拉选项被遮挡,需要给其父容器设置足够的高度才能完整显示
uni-datetime-picker、uni-popup等使用fixed定位的组件显示位置不对?
由于y-tab使用了transform: translate(0px, 0px) translateZ(0px);
,需避免fixed元素被y-tab包裹,请放在y-tabs组件的外层
如何在自定义组件中修改y-tabs组件的样式?
在微信小程序端,在自定义组件中使用了y-tabs
,需要设置styleIsolation: 'shared'
,否则在自定义组件中无法修改y-tabs
的样式
如何修改y-tabs的样式:
- 如果在
自定义组件
或页面
中引入了y-tabs,使用::v-deep
在y-tabs
所在的页面
进行修改 - 将要修改的样式放在全局样式中,但要给
y-tabs
赋予一个全局唯一的类名,避免全局样式污染 -
仅在微信小程序端,如果自定义组件中使用了
y-tabs
并想修改其样式,需要在自定义组件上配置styleIsolation: 'shared'
export default { options: { styleIsolation: 'shared' },
如何对激活标签的相邻标签设置样式 ?
直接可使用y-tab__prev
与y-tab__next
类 案例 :组合导航(侧边栏导航+滚动导航)-仿京东商品分类
侧边栏导航、滚动导航模式
- 两者内容区域的滚动方式通过
pageScroll
属性控制 - 为false时,则表示内容页是放在
scroll-view
中实现的局部滚动 案例 :滚动导航(区域滚动) | 侧边栏导航(区域滚动) - 为true时,则表示内容区域是在页面上的,跟随页面滚动而滚动(页面级滚动) 案例 :滚动导航(页面级滚动) | 侧边栏导航(页面级滚动)
- 如何取舍两者之间的使用,根据场景考虑,比如你的标签页上方有banner图,你需要在滚动过程中让标题栏浮动时,则需要页面级滚动,或者需要触发原生的上拉加载、下拉刷新事件也用该方式;
- 如果你想两种模式在一定高度内滚动,那么就使用局部滚动。
如果标签栏仍出现滚动条,请在全局样式(可放在APP.vue引入的样式文件)中添加如下css:
// 隐藏scroll-view的滚动条
::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
如果在进入页面时又跳转了其他页面,会导致y-tabs内部未初始化完成,因此底部条会错位:
- 场景:进入‘我的’页面,检测到用户未登录,跳转至登录页进行登录后又回到‘我的’页面
-
解决方法如下:
export default { onShow() { // 如果进入该页面又跳转了其他页面,则在onShow中resize一下组件,避免跳转其他页面时tabs内部未初始化完成 this.$refs?.tabs?.resize() },