HarmonyOS鸿蒙Next中大佬们,求助。如何去手搓一个banner的效果
HarmonyOS鸿蒙Next中大佬们,求助。如何去手搓一个banner的效果
- 高度50展示单条数据
- 竖向排列循环
- 3000ms自动轮循
- 可跟随手指上下滑动切换多条数据(不是手势结束后滑动,而是滑动中根据偏移量上下切换数据)
- 手指滑动时关闭自动轮循,结束后打开
- 切换数据效果流畅,像swiper一样,有一个上一条数据出去,下一个数据进来的效果
swiper控件肯定是不行,只能滑动单条数据。并且超出swiper50高度后就接收不了触摸事件了。
list的话自己也完全实现不出来上面的所有条件。
这个场景建议自定义状态机实现,不要硬套 Swiper 或 List。可以用一个固定高度 50 的 Stack 作为可视窗口,外层 clip(true),内部只渲染上一条、当前条、下一条三项。
实现思路是:1. 用 currentIndex 和 offsetY 控制三条 item 的 translateY;2. 每 3000ms 调用同一个切换函数,把下一条从 50 动画到 0、当前条从 0 动画到 -50;3. 手势开始时清掉定时器,滑动中让 offsetY 跟随手指,结束后按阈值决定回弹或切换,并重启定时器;4. 数据循环用取模处理 index。
这样能同时满足固定高度、竖向循环、自动轮播、拖动跟手、暂停自动轮播和上一条/下一条进出场动画。
更多关于HarmonyOS鸿蒙Next中大佬们,求助。如何去手搓一个banner的效果的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
谢大佬。膜拜
可以用Stack布局搭配绝对定位,结合 Gesture 手势 和 Animator (或显式动画) 自定义一套轮播逻辑。
谢谢大佬。不过楼下说的比较透彻,我先采纳他的了。
积分也不多,估计大佬您也看不上,嘿嘿
在HarmonyOS鸿蒙Next中,使用ArkTS手动实现banner效果可通过Scroll组件+手势事件完成。监听onTouch或onGestureSwipe计算滑动偏移,用@State记录当前索引,动态调整子组件translate偏移量。配合PageIndicator组件显示页码即可。
直接用 Stack 容器叠加两个 Text,通过手势 PanGesture 和属性动画实现竖向跟随切换。维护一个数据源数组、当前索引 index、纵向偏移 offsetY。设置组件高度 50,裁剪溢出内容。
@Entry
@Component
struct Banner {
@State index: number = 0
@State offsetY: number = 0
@State timer: number = -1
private data: string[] = ['A','B','C','D']
private itemHeight: number = 50
aboutToAppear() { this.startTimer() }
startTimer = () => { this.timer = setInterval(() => this.switchTo(this.index + 1, true), 3000) }
clearTimer = () => { clearInterval(this.timer); this.timer = -1 }
switchTo(next: number, animate: boolean) {
let target = (next + this.data.length) % this.data.length
if (animate) animateTo({ duration: 300, curve: Curve.EaseInOut }, () => { this.index = target; this.offsetY = 0 })
else { this.index = target; this.offsetY = 0 }
}
build() {
Stack() {
Text(this.data[(this.index - 1 + this.data.length) % this.data.length]) // 上一条(移出)
.width('100%').height(this.itemHeight)
.translate({ y: -this.itemHeight + this.offsetY })
.animation({ duration: 0 }) // 跟随手势禁止动画
Text(this.data[this.index]) // 当前(移入)
.width('100%').height(this.itemHeight)
.translate({ y: this.offsetY })
}
.height(this.itemHeight).clip(true)
.gesture(PanGesture({ direction: PanDirection.Vertical })
.onActionStart(() => this.clearTimer())
.onActionUpdate((event: GestureEvent) => {
this.offsetY = event.offsetY
let exceed = Math.abs(this.offsetY) / this.itemHeight
if (exceed >= 0.5) { // 过半切换
this.switchTo(this.index + (this.offsetY > 0 ? -1 : 1), false)
this.offsetY = 0
}
})
.onActionEnd(() => {
animateTo({ duration: 150 }, () => this.offsetY = 0) // 回弹
this.startTimer()
})
)
}
}
要点:偏移过半立即切换并重置 offset,松手回弹,手势期间 kill 定时器,结束后重启。两条 Text 一个向上退出、一个从下方进入,形成流畅切换。

