SwiftUI音乐播放器:实现跑马灯文本截断后从另一侧重新滚动
音乐播放器跑马灯文本实现方案
要实现文本到达容器右侧后截断并从左侧重新出现的跑马灯效果,需要先计算文本与容器的宽度差,再基于这个差值设置精准的动画偏移量,同时只在文本过长时触发动画。以下是优化后的代码:
首先定义一个PreferenceKey来获取文本的尺寸:
private struct TextWidthKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() } }
然后替换原有的VStack部分,实现自适应的跑马灯:
@State private var scrollOffset: CGFloat = 0 @State private var textWidth: CGFloat = 0 private let containerWidth: CGFloat = 100 VStack(alignment: .leading) { // 跑马灯文本容器 ZStack(alignment: .leading) { Text(album.songs.title ?? "") .font(.system(size: 13)) .foregroundColor(.white) .fixedSize(horizontal: true, vertical: false) .background( GeometryReader { proxy in Color.clear .preference(key: TextWidthKey.self, value: proxy.size.width) } ) .offset(x: scrollOffset) .onPreferenceChange(TextWidthKey.self) { newWidth in textWidth = newWidth // 文本过长时启动动画 if newWidth > containerWidth { let duration = newWidth / 30 // 控制滚动速度,30为每秒滚动的像素数 withAnimation(.linear(duration: duration).repeatForever(autoreverses: false)) { scrollOffset = -(newWidth - containerWidth) } } else { // 文本较短时重置偏移 scrollOffset = 0 } } } .frame(width: containerWidth, height: 18, alignment: .leading) .background(.red) Text(album.name) .font(.system(size: 9)) .foregroundColor(.gray) }
关键优化点:
- 尺寸计算:通过
GeometryReader和自定义PreferenceKey获取文本的实际宽度,对比容器宽度判断是否需要跑马灯 - 精准偏移:动画偏移量设置为
-(文本宽度 - 容器宽度),确保文本刚好完全移出容器右侧时完成一次动画循环 - 自适应触发:只有当文本宽度超过容器宽度时才启动动画,短文本保持正常显示
- 速度控制:通过
文本宽度/30动态计算动画时长,保证不同长度的文本滚动速度一致
内容的提问来源于stack exchange,提问作者AdamLeet
相关产品推荐
相关产品推荐

