鸿蒙Next Tabs实现底部导航进阶

目标:实现一个仿微信底部Tab标签随页面滑动颜色渐变的效果

最终效果:

实现思路: 1.需要用到tabs两个回调函数: **onGestureSwipe(handler: (index: number, event: TabsAnimationEvent) => void)**在页面跟手滑动过程中,逐帧触发该回调 通过这个函数回调,我们可以知道手指滑动的距离,和滑动方向,然后根据滑动距离和屏幕宽度计算一个百分比,用做修改tab的色值透明度 onAnimationStart(handler: (index: number, targetIndex: number, event: TabsAnimationEvent) => void) 通过这个函数,我们可以知道滑动触发切换成功,页面已经切换,这里我们将最终色值属性赋值 2.滑动涉及到当前选中和滑动目标页,因此需要定义两个index作为标记,用作text和image判断是否是当前页面和目标页。 3.tab的设计用stack层叠布局,下面放默认状态的布局,不进行修改,上面叠一个变化的布局,即选中状态的样式,通过改变这个布局的透明度,做到渐变 4.当前选中页tab的透明度和目标页的透明度如何根据index设置,给出一段伪代码参考

arduino 复制代码
//if (选中页==index){
//  if(目标页==index){
//    认为是没有滑动,显示正常选中状态 即 透明度=1
//  }else{
//    滑动中,选中页的图片透明度 变化范围是[1-0]
//  }
// }else{
//    if(目标页==index){
//      滑动中,目标页的图片透明度 变化范围是[0-1]
//    }else{
//      不是目标页 也不是当前页 其他页 选中状态的图片透明度 = 0
//    }
// }

实现代码:

kotlin 复制代码
import { getScreenWidth } from '../utils/DisplayUtil';
import Logger from '../utils/Logger'

@Entry
@ComponentV2
struct MainPage{
  tabsController: TabsController = new TabsController();
  pageInfos: NavPathStack = new NavPathStack()
  beginMoveTime :number = 0;
  @Local currentIndex: number = 0; //当前tab页
  @Local targetIndex: number = 0;  //目标tab页
  @Local currentOpacity:number = 1; //当前tab页 tab选中情况下的透明度  变化值1-0
  @Local targetOpacity:number = 0; //目标tab页 tab 将要被选中时的透明度 变化值0-1

  @Builder
  tabBuilder(title: string, index: number, selectedImg: Resource, normalImg: Resource) {
    Column() {
      Stack(){
        Image(normalImg)
          .width(24)
          .height(24)
          .objectFit(ImageFit.Contain)
        Image(selectedImg)
          .width(24)
          .height(24)
          .objectFit(ImageFit.Contain)
          .opacity(this.currentIndex === index?  //如果选择当前的tab 则该图片显示 不透明 如果在移动过程中 target 透明度0-1 当前1-0
            this.targetIndex===index?1:this.currentOpacity:this.targetIndex===index?this.targetOpacity:0)
      }
      Stack(){
        Text(title)
          .margin({ top: 4 })
          .fontSize(12)
          .fontColor('#9E9E9E')
        Text(title)
          .margin({ top: 4 })
          .fontSize(12)
          .fontColor('#007AFF')
          .opacity(this.currentIndex === index?
            this.targetIndex===index?1:this.currentOpacity
            :this.targetIndex===index?this.targetOpacity:0)
        //if (选中页==index){
        //  if(目标页==index){
        //    认为是没有滑动,显示正常选中状态 即 透明度=1
        //  }else{
        //    滑动中,选中页的图片透明度 变化范围是[1-0]
        //  }
        // }else{
        //    if(目标页==index){
        //      滑动中,目标页的图片透明度 变化范围是[0-1]
        //    }else{
        //      不是目标页 也不是当前页 其他页 选中状态的图片透明度 = 0
        //    }
        // ]
      }

    }
    .justifyContent(FlexAlign.Center)
    .height(52)
    .width('100%')
    .onClick(() => {
      this.currentIndex = index;
      this.targetIndex = index;
      this.tabsController.changeIndex(this.currentIndex);
    })
  }
  @Builder
  tabContentBuilder(text: string, index: number, selectedImg: Resource, normalImg: Resource) {
    TabContent() {
      Row() {
        Text(text)
          .height(300)
          .fontSize(30)
      }
      .width('100%')
      .justifyContent(FlexAlign.Center)
    }
    .backgroundColor(Color.White)
    .tabBar(this.tabBuilder(text, index, selectedImg, normalImg))
  }
  build() {
    Navigation(this.pageInfos){
      Tabs({ barPosition: BarPosition.End, controller:this.tabsController}) {
        this.tabContentBuilder('首页', 0, $r('app.media.map_tab_home_sel'), $r('app.media.map_tab_home_nor'))
        this.tabContentBuilder('消息', 1, $r('app.media.map_tab1_sel'), $r('app.media.map_tab1_nor'))
        this.tabContentBuilder('同事圈', 2, $r('app.media.map_tab2_sel'), $r('app.media.map_tab2_nor'))
        this.tabContentBuilder('通讯录', 3, $r('app.media.map_tab3_sel'), $r('app.media.map_tab3_nor'))
        this.tabContentBuilder('我的', 4, $r('app.media.map_tab4_sel'), $r('app.media.map_tab4_nor'))

      }
      .width('100%')
      .backgroundColor('#F3F4F5')
      .barHeight(52)
      .barMode(BarMode.Fixed)
      //tab切换动画时间,例如tab从0-5 直接展示5 不显示中间的滑动过程
      .animationDuration(0)
      // .scrollable(false)  //如果不想让内容滑动,可关闭滑动效果
      .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {
        //content 触发切换滑动开始的回调
        Logger.info("======onAnimationStart",'index:'+index+'targetIndex:'+targetIndex)
        this.currentIndex = targetIndex
        this.targetIndex=targetIndex
        this.currentOpacity =1
        this.targetOpacity = 0
      })
      .onGestureSwipe((index: number, event: TabsAnimationEvent) =>{
        //左划小于0 右滑大于0
        let currentOffset = event.currentOffset
        if (currentOffset>0&&index==0) {
          return
        }else if(currentOffset>0){
          this.targetIndex=index-1
        }else if (currentOffset<0&& index<5){
          this.targetIndex=index+1
        }else {
          return
        }

        // 获取屏幕的宽vp 根据手指左右滑动的距离除以屏幕的宽 计算tab图片的透明度
        let percent = Math.abs(currentOffset)/(getScreenWidth()*3/4)
        if (percent>1) {
          percent=1
        }
        if (percent<0) {
          percent=0
        }
        this.currentOpacity = 1-percent
        this.targetOpacity =percent;
        Logger.info("======onGestureSwipe",'index:'+index+'currentOffset:'+event.currentOffset+'percent:'+percent)
      })

    }.hideTitleBar(true).hideToolBar(true)
    .width('100%')
    .height('100%')
  }
}

###注意: 官方文档中onAnimationStart 这个回调函数下面写了,当animationDuration为0时动画关闭,不触发该回调。

这个解释是不严谨的,当animationDuration=0时,只是点击tab切换页签不回调这个函数,滑动切换还是会回调这个函数的。

相关推荐
nashane6 小时前
HarmonyOS 6学习:CapsLock键失效诊断与长截图完整实现指南
学习·华为·harmonyos
richard_yuu8 小时前
鸿蒙心理测评模块实战|PHQ-9/GAD7双量表答题、实时计分与结果本地化存储
华为·harmonyos
不爱吃糖的程序媛11 小时前
2026年Electron 鸿蒙PC环境搭建指南
人工智能·华为·harmonyos
nashane11 小时前
HarmonyOS 6学习:长截图功能开发中的滚动拼接与权限处理实战
人工智能·华为·harmonyos
大师兄666812 小时前
从零开发一个 HarmonyOS 输入法——KikaInputMethod 完整拆解
harmonyos·服务卡片·harmonyos6·formkit
Python私教17 小时前
鸿蒙 NEXT 也能接 MCP?用 ArkTS 跑通 AI Agent 工具链
人工智能·华为·harmonyos
Swift社区20 小时前
分布式能力在鸿蒙 PC 上到底怎么用?
分布式·华为·harmonyos
nashane1 天前
HarmonyOS 6学习:外接键盘CapsLock与长截图功能的实战调试与完整解决方案
学习·华为·计算机外设·harmonyos
aqi002 天前
一文理清 HarmonyOS 6.0.2 涵盖的十个升级点
android·华为·harmonyos·鸿蒙·harmony