目录
前言
1.需求分析
2.具体实现
2.1效果展示
2.1.1常见样式
2.1.2 Tab 宽度等分
2.1.3 Page 内容不一致
2.1.4 Index 覆盖 Tab
2.2实现步骤
2.2.1布局分析
2.2.2功能实现
3.使用步骤
3.1基本使用
3.2属性说明
4.注意事项
5.最后
最近被分配到做项目小程序端的任务,做到原生端常见的 TabLayout + ViewPager 实现的 Tab 切换页面时,发现小程序未提供类似可以直接使用的 TabLayout 组件。
网上搜寻发现小程序端需要实现 Tab 切换效果,多是通过 scroll-view 和 swiper 联动实现,并且 指示器 没有过渡效果,多是闪现跳到下一个 Tab 。因此自行研究实现接近原生端的 TabLayout 组件。
下图是小程序 小米Lite 的 Tab 切换效果:切换 Page 时,Tab 下方的指示器(红色横条)是没有滚动效果而是直接闪现到下一个 Page 的,这也是市面上小程序常见的 Tab 切换效果。
原生端 TabLayout 常见的功能就是我们的目标效果,所以 目标效果如下:
- 指示器(Index)具有切换过渡效果;
- 指示器(Index)需可自定义,常见的有:可固定宽度、可与 Tab 内容等宽、可覆盖在 Tab 上;
- Tab 可自定义、可支持自动适应父控件宽度等分 Tab 宽度;
- 当 Tab 总宽度超出父控件宽度时,Tab 行支持滚动且切换 Page 时保证当前 Tab 可见;
- 支持 Page 切换监听
通过了解小程序组件及技术支持,选定通过 scroll-view 、swiper 、swiper-item 、movable-area 、movable-view 配合 插槽 和 抽象节点 来实现自定义组件 tab-layout 。
包含特点:
- Page 懒加载;
- Page 切换时 Tab 自动跟随滚动;
- Page 内 scroll-view 的滚动处理;
包含特点:
- Tab 宽度等分组件宽度;
- 跳转指定 Position = 2 ;
- 监听页面切换;
包含特点:
- 根据 Position 变化调整 Page 布局;
- Index 宽度与 Tab 宽度保持一致;
- Tab 总宽度未填满 TabLayout 时居中显示;
包含特点:
- Index 悬浮覆盖在 Tab 上;
- Tab 与 Index 之间插入固定 View
- Page 禁止左右滑动;
使用可横向滑动的 scroll-view 作为 Tab 和 指示器(Index)的容器
Tab 的具体内容与样式,由抽象节点 item-tab 决定
Index 滑动区域由 movable-area 实现,长度应与 Tab 栏总宽度一致,高度由自定义属性 indexAreaHeight 赋值决定
Index 的展示区域由 movable-view 实现,并提供插槽 slot name="index" 让用户可自定义 Index 样式(用户也可采用自定义属性 indexStyle 来直接设置 movable-view 的 style 从而实现 Index 的样式).
各 Tab 对应的 Page,由被 swiper 包裹的 swiper-item 充当容器,通过抽象节点 item-page 决定内容
具体如下图所示 :
自定义组件 TabLayout 的布局代码(点击跳转查看源码):
1.在组件生命周期来到 attached 方法时:
- 获取所有 Tab 的宽度并记录,用设置 Index 的显示区域长度及活动区域长度
- 通过自定属性 targetIndex 判断是否需要进行页面切换跳转指定 page
2.通过监听 Tab 的点击事件,促使 swiper 切换页面;
3.通过 swiper 组件的 bindchange 方法,监听页面切换事件;
4.在页面切换的时候:
- 计算并移动 Index(movable-view)到指定 Tab 位置
- 计算 scroll-view 应该横向滚动的距离(为使得选中 Tab 和 Index 能始终保持可见)
主要实现(点击跳转查看源码)
1.复制 tab-layout 组件到项目中(点击跳转至源码 TabLayout 目录)
2.自定义 Tab 与 Page 组件,并声明 item、position 和 currentIndex 三个自定义属性
3.在页面的配置文件中引用 tab-layout 、自定义的 Tab 和 Page 组件
{
"usingComponents": {
"tab-layout":"/components/tab-layout/tab-layout",
"item-page":"./item-page/item-page",
"item-tab":"./item-tab/item-tab"
}
}
4.在布局文件中使用 TabLayout 组件,并通过抽象节点 generic:item-tab 和 generic:item-page 分别与自定义的 Tab 和 Page 绑定
5.使用自定义属性 indexAreaHeight 为 Index 及其活动区域设置高度
6.使用自定义属性 tabList 设置数据源,根据数据源将自动生成对应数量的 Tab 和 Page
7.按需选择可采用插槽 slot="index" 或自定义属性 indexStyle 设置 Index 的样式
布局文件 index.wxml 中:
样式文件 index.wxss 中
.index{
width: 60rpx;
height: 5rpx;
background: linear-gradient(to right, #00CFFF, #00A1FF);
border-radius: 2rpx;
}
布局文件 index.wxml 中
属性 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
tabList |
array |
[] | 是 | 数据源,根据数据源将自动生成对应 Tab 和 Page 数 |
isTabCenter |
boolean |
false |
否 | Tab 是否居中显示,Tab 总宽度未填满 TabLayout 时,可通过该属性讲 Tab 居中显示 |
targetIndex |
number |
0 | 否 | 选中 Tab 下标,可通过该属性跳转指定 Tab 与 Page |
indexAreaHeight |
number |
10 | 是 | Index 及其活动区域高度,单位为 px |
indexStyle |
string |
否 | 可通过该属性设置 index 的 style | |
indexAreaStyle |
string |
否 | 可通过该属性设置 index 活动区域的 style | |
isStopTouchMove |
boolean | false | 否 | 是否禁止左右滑动, true 禁止 false 允许 |
isTabSpaceEqual |
boolean |
false |
否 | 是否根据组件宽度等分 Tab 宽度 |
isSetIndexPositionAbsolute |
boolean | false | 否 | 是否设置 Index 悬浮在 Tab 上 |
pageChange | eventhandle | 否 | 页面切换监听事件 | |
index |
slot (插槽) |
否 | 自定义 Index view 插槽 | |
subContent |
slot (插槽) |
否 | 位于 Tab 与 Page 之间的插槽,不在 Tab/Page 内,即不会随着 Tab/Page 变动或切换 | |
item-tab |
generic (抽象节点) |
是 | 插入自定义 Tab View 的抽象节点,决定 Tab 样式与内容 | |
item-page |
generic (抽象节点) |
是 | 插入自定义 Page View 的抽象节点,决定 Page 样式与内容 |
PS:还有一个很重要的方法 onPageUpdata ,用于抽象节点 item-tab 和 item-page 通知父节点 tab-layout 刷新数据,在子控件中通过 this.triggerEvent("updata") 触发
可能出现问题:
1.当 item-page 中存在竖直滚动的 scroll-view 时出现滑动冲突该如何解决?
在 item-page 组件 attached 方法中按需为 scroll-view 设置固定高度或占满屏幕剩余位置(点击跳转查看参考写法)
2.当 item-page 或 item-tab 中调用 this.setData ( ) 之后,发现自定义属性 item 获取值为 null ?
出现该种情况,应在 this.setData ( ) 之后,执行 this.triggerEvent("updata") 触发 tab-layout 的 onPageUpdata 方法重新得到 item 值
3.如何实现 " 懒加载 " ,即当 Tab 首次被选中时,才进行对应 Page 的数据加载?
为自定义组件 item-page 设置一个懒加载标志位暂定为 isLoadData ,通过订阅自定义属性 currentIndex ,在 currentIndex 属性变化或组件进行到 attached 生命周期时,通过判断 isLoadData 和 currentIndex 是否与 position 相等来进行数据加载并调整标志位(点击跳转查看参考写法)
在小程序越来越普及的现状下,如何使得小程序能给用户带来更完善的显示效果和使用体验,是每一个开发者都应该力尽其责的事。鉴于本人当前对小程序和网页端的熟悉程度,该组件或许还存在很多瑕疵,如有更好的见解或建议,欢迎留言。