单击按钮时如何为我的胶囊进度设置动画?

How can I animate my capsule's progress when a button is clicked?

希望你们一切都好。 我是这个社区的新手,我希望我们能互相帮助成长。

我目前正在使用 swiftUI 构建一个 MacOS 应用程序,我有这个胶囊形状,里面有一个水动画。水动画只是水平移动的波浪,使用可动画数据和路径创建。我的问题是,我在屏幕上还有另外两个按钮,一个播放按钮和一个停止按钮。他们应该分别开始向胶囊注水和停止,他们确实这样做了,但他们应该用动画来完成,但事实并非如此。

下面是我的代码以获取更多详细信息。提前致谢。

GeometryReader { geometry in
                                
    VStack {

        Spacer()
        
        BreathingWave(progress: $progressValue, phase: phase)
            .fill(Color(#colorLiteral(red: 0.14848575, green: 0.6356075406, blue: 0.5744615197, alpha: 1)))
            .clipShape(Capsule())
            .border(Color.gray, width: 3)
            .frame(width: geometry.size.width / 12)
            .onAppear {
                withAnimation(.linear(duration: 1).repeatForever(autoreverses: false)) {
                    self.phase = .pi * 2
                }
            }
        
        HStack {

            Spacer()

            Image(systemName: "play.circle.fill")
                .resizable()
                .renderingMode(.template)
                .frame(width: 50, height: 50)
                .foregroundColor(Color(#colorLiteral(red: 0.14848575, green: 0.6356075406, blue: 0.5744615197, alpha: 1)))
                .scaleEffect(scalePlay ? 1.25 : 1)
                .onHover(perform: { scalPlay in
                    withAnimation(.linear(duration: 0.1)) {
                        scalePlay = scalPlay
                    }
                })
                .onTapGesture {
                    withAnimation(
                        .easeInOut(duration: duration)
                        .repeatForever(autoreverses: true)) {
                            if(progressValue < 1) {
                                progressValue += 0.1
                            }
                            else {
                                progressValue = progressValue
                            }
                        }
                }

            Spacer()

            Image(systemName: "stop.circle.fill")
                .resizable()
                .renderingMode(.template)
                .frame(width: 50, height: 50)
                .foregroundColor(Color(#colorLiteral(red: 0.14848575, green: 0.6356075406, blue: 0.5744615197, alpha: 1)))
                .scaleEffect(scaleStop ? 1.25 : 1)
                .onHover(perform: { scalStop in
                    withAnimation(.linear(duration: 0.1)) {
                        scaleStop = scalStop
                    }
                })
                .onTapGesture {
                    withAnimation(.linear) {
                        progressValue = 0.0
                    }
                }

            Spacer()

        }
        .padding(.bottom, 50)
    }
}

这是呼吸波的代码

struct BreathingWave: Shape {
    
    @Binding var progress: Float
    var applitude: CGFloat = 10
    var waveLength: CGFloat = 20
    var phase: CGFloat

    var animatableData: CGFloat {
        get { phase }
        set { phase = newValue }
    }
    
    func path(in rect: CGRect) -> Path {
        
        var path = Path()
        
        let width = rect.width
        let height = rect.height
        let minWidth = width / 2
        let progressHeight = height * (1 - CGFloat(progress))
        
        path.move(to: CGPoint(x: 0, y: progressHeight))
        
        for x in stride(from: 0, to: width + 5, by: 5) {
            let relativeX = x / waveLength
            let normalizedLength = (x - minWidth) / minWidth
            let y = progressHeight + sin(phase + relativeX) * applitude * normalizedLength
            path.addLine(to: CGPoint(x: x, y: y))
        }
        path.addLine(to: CGPoint(x: width, y: progressHeight))
        path.addLine(to: CGPoint(x: width, y: height))
        path.addLine(to: CGPoint(x: 0, y: height))
        path.addLine(to: CGPoint(x: 0, y: progressHeight))
        
        return path
    }
}

您 运行 遇到的问题只是您从未告诉 BreathingWave 如何为进度设置动画。当你真的想让它在二维中动画时,你设置了一个单一维度的动画。解决方法很简单:使用 AnimatablePair 将变量提供给 animatableData.

struct BreathingWave: Shape {
    
    var applitude: CGFloat = 10
    var waveLength: CGFloat = 20
    // progress is no longer a Binding. Using @Binding never did anything
    // as you were not changing the value to be used in the parent view.
    var progress: Float
    var phase: CGFloat

    var animatableData: AnimatablePair<Float, CGFloat> { // <- AnimatablePair here
        get { AnimatablePair(progress, phase) } // <- the getter returns the pair
        set { progress = newValue.first // <- the setter sets each individually from
            phase = newValue.second     //    the pair.
        }
    }
    
    func path(in rect: CGRect) -> Path {
        ...
    }
}

最后,你现在这样称呼它:

BreathingWave(progress: progressValue, phase: phase)

最后一件事。以后请提供一个Minimal Reproducible Example (MRE)。有很多代码不需要调试它,我不得不将结构头实现为 运行 它。尽可能多地去掉,但给我们一些我们可以复制、粘贴和 运行.

的东西