小程序实现贴近原生端的 TabLayout + ViewPager 切换效果

目录

前言

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 组件。

1.需求分析

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

小程序 小米Lite Tab切换效果

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

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

通过了解小程序组件及技术支持,选定通过 scroll-view swiperswiper-itemmovable-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.3效果演示 -- Page 内容不一致

 包含特点:

  • 根据 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 决定内容

具体如下图所示 :

小程序实现贴近原生端的 TabLayout + ViewPager 切换效果_第1张图片 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 目录)

小程序实现贴近原生端的 TabLayout + ViewPager 切换效果_第2张图片 3.1.1使用步骤 -- 复制组件源码

2.自定义 Tab 与 Page 组件,并声明 item、position 和 currentIndex 三个自定义属性

小程序实现贴近原生端的 TabLayout + ViewPager 切换效果_第3张图片 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 宽度

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") 触发

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

 

 

你可能感兴趣的:(小程序)