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

SwiftUI中绘制路径后如何为Swift Bird添加填充动画

SwiftUI 实现路径绘制动画后正确填充形状

问题根源

你遇到的填充异常是因为trim裁剪后的路径是非闭合的分段路径,直接对其同时应用stroke和fill时,SwiftUI的填充规则会错误计算路径包围区域,导致内部黑块、颜色溢出等问题。

解决方案

将描边动画和填充效果拆分为两个独立图层,通过状态变量控制动画流程,避免在同一个裁剪后的Shape上混合使用stroke和fill。以下是两种可行实现方式:

方式1:描边动画完成后显示填充层

先完整执行路径绘制动画,动画结束后再淡入显示填充好的完整形状:

// 假设你已定义的SwiftBird形状
struct SwiftBird: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        // 替换为你实际的SwiftBird路径代码
        path.move(to: CGPoint(x: rect.midX, y: rect.top))
        path.addArc(center: CGPoint(x: rect.midX, y: rect.midY), radius: rect.height/3, startAngle: .degrees(0), endAngle: .degrees(360), clockwise: false)
        path.addLine(to: CGPoint(x: rect.midX + rect.width/4, y: rect.bottom))
        path.addLine(to: CGPoint(x: rect.midX - rect.width/4, y: rect.bottom))
        path.closeSubpath()
        return path
    }
}

struct BirdAnimationView: View {
    @State private var trimProgress: CGFloat = 0
    @State private var showFill = false
    
    var body: some View {
        ZStack {
            // 填充层:动画完成后淡入
            if showFill {
                SwiftBird()
                    .fill(Color.orange)
                    .transition(.opacity)
            }
            
            // 描边动画层
            SwiftBird()
                .trim(from: 0, to: trimProgress)
                .stroke(Color.black, lineWidth: 2)
                .onAppear {
                    // 执行路径绘制动画
                    withAnimation(.easeInOut(duration: 2)) {
                        trimProgress = 1
                    } completion: {
                        // 动画结束后显示填充层
                        withAnimation(.easeIn(duration: 0.5)) {
                            showFill = true
                        }
                    }
                }
        }
        .frame(width: 200, height: 200)
    }
}

方式2:描边与填充同步过渡

让描边随进度逐步显示,同时填充层随进度逐步淡入,实现无缝过渡效果:

struct BirdAnimationView: View {
    @State private var trimProgress: CGFloat = 0
    
    var body: some View {
        ZStack {
            // 填充层:随进度淡入
            SwiftBird()
                .fill(Color.orange)
                .opacity(trimProgress)
            
            // 描边动画层:随进度显示,同时逐步淡出
            SwiftBird()
                .trim(from: 0, to: trimProgress)
                .stroke(Color.black, lineWidth: 2)
                .opacity(1 - trimProgress)
        }
        .frame(width: 200, height: 200)
        .onAppear {
            withAnimation(.easeInOut(duration: 2)) {
                trimProgress = 1
            }
        }
    }
}

关键注意点

  • 永远不要在trim后的Shape上同时使用stroke和fill,这会触发填充规则的异常计算;
  • 若需要跟随路径绘制进度的"渐进填充"效果,需要自定义AnimatableShape来控制填充区域,这需要更复杂的路径裁剪逻辑,上述两种方式是最简洁的通用解决方案。

内容的提问来源于stack exchange,提问作者batman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 00:15:59