You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 08:32:51