鸿蒙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切换页签不回调这个函数,滑动切换还是会回调这个函数的。

相关推荐
coder_pig2 小时前
跟🤡杰哥一起学Flutter (三十四、玩转Flutter手势✋)
前端·flutter·harmonyos
simple丶3 小时前
【HarmonyOS】鸿蒙蓝牙连接与通信技术
harmonyos·arkts·arkui
前端世界4 小时前
HarmonyOS开发实战:鸿蒙分布式生态构建与多设备协同发布全流程详解
分布式·华为·harmonyos
Jalor5 小时前
Flutter + 鸿蒙 | Flutter 跳转鸿蒙原生界面
flutter·harmonyos
zhanshuo6 小时前
开发者必看!如何在HarmonyOS中快速调用摄像头功能
harmonyos
HMSCore6 小时前
借助HarmonyOS SDK,《NBA巅峰对决》实现“分钟级启动”到“秒级进场”
harmonyos
zhanshuo6 小时前
鸿蒙UI开发全解:JS与Java双引擎实战指南
前端·javascript·harmonyos
HarmonyOS小助手7 小时前
闯入鸿蒙:浪漫、理想与「草台班子」
harmonyos·鸿蒙·harmonyos next·鸿蒙生态
xq95277 小时前
flutter 鸿蒙化插件开发横空出世
harmonyos
HarmonyOS_SDK7 小时前
借助HarmonyOS SDK,《NBA巅峰对决》实现“分钟级启动”到“秒级进场”
harmonyos