微信小程序实现 TabLayout 并带有过渡效果

前言

  最近被分配到做项目小程序端的任务,做到原生端常见的 TabLayout + ViewPager 实现的 Tab 切换页面时,发现小程序未提供类似可以直接使用的 TabLayout 组件。
  网上搜寻发现小程序端需要实现 Tab 切换效果,多是通过 scroll-viewswiper 联动实现,并且 指示器 没有过渡效果,多是闪现跳到下一个 Tab 。因此自行研究实现接近原生端的 tab-layout 组件。

1.需求分析

  下图是小程序 小米Lite 的 Tab 切换效果:切换 Page 时,Tab 下方的指示器(红色横条)是没有滚动效果而是直接闪现到下一个 Page 的,这也是市面上小程序常见的 Tab 切换效果。

小程序 小米Lite Tab切换效果

原生端 TabLayout 常见的功能就是我们的目标效果,所以 目标效果 如下:

  • 指示器(Index)具有切换过渡效果;
  • 指示器(Index)需可自定义,常见的有:可固定宽度、可与 Tab 内容等宽、可覆盖在 Tab 上;
  • Tab 可自定义、可支持自动适应父控件宽度等分 Tab 宽度;
  • 当 Tab 总宽度超出父控件宽度时,Tab 行支持滚动且切换 Page 时保证当前 Tab 可见;
  • 支持 Page 切换监听;

  通过了解小程序组件及技术支持,选定通过 scroll-view 、swiper 、swiper-item 、movable-area 、movable-view 配合 插槽 和 抽象节点 来实现自定义组件 tab-layout 。

2.具体实现

2.1效果展示

2.1.1常见样式

2.1.1效果演示 -- 常见样式

包含特点

  • Page 懒加载;
  • Page 切换时 Tab 自动跟随滚动;
  • Page 内 scroll-view 的滚动处理;

2.1.2 Tab 宽度等分

2.1.2效果演示 -- Tab 宽度等分

包含特点

  • Tab 宽度等分组件宽度;
  • 跳转指定 Position = 2 ;
  • 监听页面切换;

2.1.3 Page 内容不一致

2.1.2效果演示 -- Tab 宽度等分

包含特点

  • 根据 Position 变化调整 Page 布局;
  • Index 宽度与 Tab 宽度保持一致;
  • Tab 总宽度未填满 TabLayout 时居中显示;

2.1.4 Index 覆盖 Tab

2.1.4效果演示 -- Index 覆盖 Tab

包含特点

  • Index 悬浮覆盖在 Tab 上;
  • Tab 与 Index 之间插入固定 View
  • Page 禁止左右滑动;

2.2实现步骤

2.2.1布局分析

  1. 使用可横向滑动的 scroll-view 作为 Tab 和 指示器(Index)的容器
  2. Tab 的具体内容与样式,由抽象节点 item-tab 决定
  3. Index 滑动区域由 movable-area 实现,长度应与 Tab 栏总宽度一致,高度由自定义属性 indexAreaHeight 赋值决定
  4. Index 的展示区域由 movable-view 实现,并提供插槽 slot name="index" 让用户可自定义 Index 样式(用户也可采用自定义属性 indexStyle 来直接设置 movable-view 的 style 从而实现 Index 的样式).
  5. 各 Tab 对应的 Page,由被 swiper 包裹的 swiper-item 充当容器,通过抽象节点 item-page 决定内容

具体如下图所示 :

2.2自定义组件布局分析
自定义组件 TabLayout 的布局代码(点击跳转查看源码):



  
  
    
      
      
        
        
          
          
        
      
      
      
        
          
          
        
      
      
    
  
  
  
  
    
      
      
    
  

2.2.2功能实现

  1. 在组件生命周期来到 attached 方法时:
     获取所有 Tab 的宽度并记录,用设置 Index 的显示区域长度及活动区域长度
     通过自定属性 targetIndex 判断是否需要进行页面切换跳转指定 page
  2. 通过监听 Tab 的点击事件,促使 swiper 切换页面;
  3. 通过 swiper 组件的 bindchange 方法,监听页面切换事件;
  4. 在页面切换的时候:
     计算并移动 Index(movable-view)到指定 Tab 位置
     计算 scroll-view 应该横向滚动的距离(为使得选中 Tab 和 Index 能始终保持可见)

主要实现(点击跳转查看源码)

3.使用步骤

3.1基本使用

  1.复制 tab-layout 组件到项目中(点击跳转至源码 TabLayout 目录)

3.1.1使用步骤 -- 复制组件源码

  2.自定义 Tab 与 Page 组件,并声明 item、position 和 currentIndex 三个自定义属性
3.1.2使用步骤 -- 自定义组件 Tab 和 Page

  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 的样式

  • 采用插槽 slot = "index" 方式设置 Index 样式

  布局文件 index.wxml 中:


  
    
  

  样式文件 index.wxss 中

.index{
  width: 60rpx;
  height: 5rpx;
  background: linear-gradient(to right, #00CFFF, #00A1FF);
  border-radius: 2rpx;
}
  • 采用自定义属性 indexStyle 方式设置 Index 样式

  布局文件 index.wxml 中



3.2属性说明

属性 类型 默认值 必填 说明
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 宽度
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") 触发

4.注意事项

可能出现问题:
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 相等来进行数据加载并调整标志位(点击跳转查看参考写法)

5.最后

  在小程序越来越普及的现状下,如何使得小程序能给用户带来更完善的显示效果和使用体验,是每一个开发者都应该力尽其责的事。鉴于本人当前对小程序和网页端的熟悉程度,该组件或许还存在很多瑕疵,如有更好的见解或建议,欢迎留言。

  • 小程序代码片段:https://developers.weixin.qq.com/s/KnxMSmmx7Yqh
  • 源码及 Demo 地址:https://github.com/ziwenL/TabLayout

你可能感兴趣的:(微信小程序实现 TabLayout 并带有过渡效果)