🎯 案例集合Tabs:自定义tabs左右边缘渐隐,切换动画衔接 + 更多按钮
🌍 案例集合Tabs
🏷️ 效果图
📖 参考
🧩 拆解
- 自定义tabs(V1-V2组件都可用)使用fadingEdge Api 14
@Component
export struct tabsMoreButAndFadingEdge {
private mockData: string[] = ['购物', '体育', '财经', '服装', '军事', '政治', '居家', '国际', '科技', '城市', '景点']
private listScroller: ListScroller = new ListScroller()
@State selectIdx: number = 0
@State animationStartIdx: number = 0
@Builder
tabBuilder(label: string, idx: number) {
Text(label)
.width(60)
.height(40)
.borderRadius('50%')
.fontSize(16)
.fontColor(this.animationStartIdx === idx ? Color.White : Color.Black)
.backgroundColor(this.animationStartIdx === idx ? Color.Orange : '#80e0dede')
.textAlign(TextAlign.Center)
.onClick(() => {
this.listScroller.scrollToIndex(idx, true, ScrollAlign.CENTER)
this.selectIdx = this.animationStartIdx = idx
})
}
build() {
Column() {
Stack({ alignContent: Alignment.TopStart }) {
Tabs({ index: this.selectIdx }) {
ForEach(this.mockData, (item: string) => {
TabContent() {
Column() {
Text(item)
}
.width('100%')
.height('100%')
.backgroundColor('#66e2dcdc')
.justifyContent(FlexAlign.Start)
}
})
}
.barHeight(0)
.padding({ top: 50 }) // 关键:这里解决自定义tabs上面缺失的barHeight高度
.onChange((idx: number) => {
this.selectIdx = idx
})
// 关键:切换动画开始时触发该回调:解决切换tabs延迟的问题
.onAnimationStart((idx: number, targetIndex: number) => {
if (idx === targetIndex) {
return
}
this.listScroller.scrollToIndex(targetIndex, true, ScrollAlign.CENTER)
this.animationStartIdx = targetIndex
})
Row({ space: 10 }) {
Row() {
List({ scroller: this.listScroller, space: 10 }) {
ForEach(this.mockData, (item: string, idx: number) => {
this.tabBuilder(item, idx)
})
}
.width('100%')
.height(40)
.listDirection(Axis.Horizontal)
.scrollBar(BarState.Off)
.fadingEdge(true, { fadingEdgeLength: LengthMetrics.vp(40) })
}
.height(40)
.layoutWeight(1)
Row() {
Image($r('app.media.startIcon'))
.width(20)
.aspectRatio(1)
}
.width(40)
.aspectRatio(1)
.backgroundColor('#80e0dede')
.borderRadius('50%')
.justifyContent(FlexAlign.Center)
.onClick(() => this.getUIContext().getPromptAction().showToast({ message: '周二周二浑浑噩噩' }))
}
.width('100%')
.height(40)
}
}
.width('100%')
.height('100%')
}
}
- 自定义tabs(V1-V2组件都可用)不使用fadingEdge 适合更低版本
@Component
export struct tabsMoreButAndFadingEdge {
private mockData: string[] = ['购物', '体育', '财经', '服装', '军事', '政治', '居家', '国际', '科技', '城市', '景点']
private listScroller: ListScroller = new ListScroller()
@State selectIdx: number = 0
@State animationStartIdx: number = 0
@State startFadingEdge: boolean = false
@State endFadingEdge: boolean = false
@Builder
tabBuilder(label: string, idx: number) {
Text(label)
.width(60)
.height(40)
.borderRadius('50%')
.fontSize(16)
.fontColor(this.animationStartIdx === idx ? Color.White : Color.Black)
.backgroundColor(this.animationStartIdx === idx ? Color.Orange : '#80e0dede')
.textAlign(TextAlign.Center)
.onClick(() => {
this.listScroller.scrollToIndex(idx, true, ScrollAlign.CENTER)
this.selectIdx = this.animationStartIdx = idx
})
}
build() {
Column() {
Stack({ alignContent: Alignment.TopStart }) {
Tabs({ index: this.selectIdx }) {
ForEach(this.mockData, (item: string) => {
TabContent() {
Column() {
Text(item)
}
.width('100%')
.height('100%')
.backgroundColor('#66e2dcdc')
.justifyContent(FlexAlign.Start)
}
})
}
.barHeight(0)
.padding({ top: 50 }) // 关键:这里解决自定义tabs上面缺失的barHeight高度
.onChange((idx: number) => {
this.selectIdx = idx
})
// 关键:切换动画开始时触发该回调:解决切换tabs延迟的问题
.onAnimationStart((idx: number, targetIndex: number) => {
if (idx === targetIndex) {
return
}
this.listScroller.scrollToIndex(targetIndex, true, ScrollAlign.CENTER)
this.animationStartIdx = targetIndex
})
Row({ space: 10 }) {
Stack() {
Row() {
List({ scroller: this.listScroller, space: 10 }) {
ForEach(this.mockData, (item: string, idx: number) => {
this.tabBuilder(item, idx)
})
}
.width('100%')
.height(40)
.listDirection(Axis.Horizontal)
.scrollBar(BarState.Off)
.onScrollIndex(() => {
this.startFadingEdge = true
this.endFadingEdge = true
})
.onReachStart(() => {
this.startFadingEdge = false
this.endFadingEdge = true
})
.onReachEnd(() => {
this.startFadingEdge = true
this.endFadingEdge = false
})
}
Row() {
Text()
.width(60)
.height(40)
.linearGradient({
direction: GradientDirection.Left,
colors: [['#00ffffff', 0.0], ['#ffffffff', 1.0]]
})
.visibility(this.startFadingEdge ? Visibility.Visible : Visibility.Hidden)
Blank()
Text()
.width(60)
.height(40)
.linearGradient({
direction: GradientDirection.Right,
colors: [['#00ffffff', 0.0], ['#ffffffff', 1.0]]
})
.visibility(this.endFadingEdge ? Visibility.Visible : Visibility.Hidden)
}
.width('100%')
.height(40)
.hitTestBehavior(HitTestMode.Transparent) // 关键:自身和子节点均响应触摸测试,不会阻塞兄弟节点和祖先节点的触摸测试
}
.height(40)
.layoutWeight(1)
Row() {
Image($r('app.media.startIcon'))
.width(20)
.aspectRatio(1)
}
.width(40)
.aspectRatio(1)
.backgroundColor('#80e0dede')
.borderRadius('50%')
.justifyContent(FlexAlign.Center)
.onClick(() => this.getUIContext().getPromptAction().showToast({ message: '周二周二浑浑噩噩' }))
}
.width('100%')
.height(40)
}
}
.width('100%')
.height('100%')
}
}
🌸🌼🌺