SwiftUI骨架加载器模糊覆盖动画无法全程流畅播放求助
SwiftUI骨架加载器平滑滑动动画实现方案
我在SwiftUI中实现了一个骨架加载器视图,期望让模糊覆盖层从左侧平滑滑入、右侧平滑滑出。但当前代码在浅色模式下的效果存在问题:动画的开头和结尾无法平滑过渡,会无动画地插入覆盖层,播放完大部分动画后又无动画地移除它。
原代码如下:
struct SkeletonLoader: View { @State private var isAnimating: Bool = false let radius: CGFloat = 50 var body: some View { GeometryReader { geo in Rectangle() .fill(Color(CGColor(gray: 0, alpha: 0.15))) .overlay( Rectangle() .fill(Color(CGColor(gray: 0, alpha: 0.6))) .frame(width: geo.size.width, height: 1000) .blur(radius: radius) .position(x: isAnimating ? geo.size.width + 180 + radius : -200 - radius) ) .onAppear { withAnimation(.easeIn(duration: 6.5).repeatForever(autoreverses: false)) { isAnimating.toggle() } } } .clipped() } }
奇怪的是,移除.clipped()修饰符可以解决动画流畅性问题,但我需要保留该修饰符以防止模糊效果超出视图范围。猜测问题在于:当矩形的position移出屏幕时,SwiftUI就不再绘制它,即便此时还有模糊效果需要完成动画。
问题根源
clipped()会裁剪掉父视图边界外的所有内容,而原代码中覆盖层的position设置让它在动画首尾阶段完全移出了父视图范围,导致SwiftUI提前停止绘制该视图,中断了模糊效果的动画过渡。
修复方案
通过调整覆盖层的尺寸和动画偏移范围,确保模糊区域在整个动画过程中始终处于父视图的绘制范围内,同时保留.clipped():
struct SkeletonLoader: View { @State private var isAnimating: Bool = false let radius: CGFloat = 50 var body: some View { GeometryReader { geo in Rectangle() .fill(Color(CGColor(gray: 0, alpha: 0.15))) .overlay( // 加宽覆盖层,让模糊边缘在滑动时不会被提前裁剪 Rectangle() .fill(Color(CGColor(gray: 0, alpha: 0.6))) .frame(width: geo.size.width + radius * 2, height: geo.size.height) .blur(radius: radius) // 调整偏移范围,确保覆盖层始终有部分区域在父视图内 .offset(x: isAnimating ? geo.size.width + radius : -geo.size.width - radius) ) .onAppear { // 改用linear动画让滑动更均匀,可根据需求调整时长和曲线 withAnimation(.linear(duration: 1.5).repeatForever(autoreverses: false)) { isAnimating.toggle() } } } .clipped() } }
关键调整点
- 覆盖层尺寸:将宽度设置为父视图宽度加两倍模糊半径,保证模糊效果的边缘在滑动过程中始终能被父视图捕获。
- 动画偏移范围:起始偏移为
-geo.size.width - radius,结束偏移为geo.size.width + radius,避免覆盖层完全移出父视图范围,确保动画全程都能被绘制。 - 动画曲线:替换
easeIn为linear让滑动动画更均匀,你可以根据需求调整动画时长或换回原曲线。
内容的提问来源于stack exchange,提问作者Stoic
相关产品推荐
相关产品推荐

