使用 Button 触发的动画停止添加 onAppear 的 repeatForever 动画

Posted

技术标签:

【中文标题】使用 Button 触发的动画停止添加 onAppear 的 repeatForever 动画【英文标题】:Animation triggered using a Button stops a repeatForever animation added onAppear 【发布时间】:2020-10-13 11:49:20 【问题描述】:

我想构建一个进度视图,看起来像一个随着进度增加而注满水的圆圈。

为此,我制作了一个自定义Shape 来创建水,并添加了一个动画,该动画将永远重复到Shape 上的波浪效果。 有了这个,我想在进度增加时添加一个动画来模拟水位增加。

问题是,当我触发最后一个动画时,它添加的动画 onAppear 停止工作。 有没有办法解决这个问题,所以两个动画都在结合,然后repeatForever 一个永远不会停止? 这是一个例子:

这是完整的代码:

struct WaterWaveView: View 
    @State var progress: CGFloat = 0.1
    @State var phase: CGFloat = 0.5

    var body: some View 
        VStack 
            WaterWave(progress: self.progress, phase: self.phase)
                .fill(Color.blue)
                .clipShape(Circle())
                .frame(width: 250, height: 250)
                .onAppear 
                    withAnimation(Animation.linear(duration: 1)
                                    .repeatForever(autoreverses: false)) 
                        self.phase = .pi * 2
                    
                
            Button("Add") 
                withAnimation(.easeOut(duration: 1)) 
                    self.progress += 0.1
                
            
            Button("Reset") 
                self.progress = 0.0
            
        
    


struct WaterWave: Shape 
    var progress: CGFloat
    let amplitude: CGFloat = 10
    let waveLength: CGFloat = 20
    var phase: CGFloat

    var animatableData: AnimatablePair<CGFloat, CGFloat> 
        get  AnimatablePair(phase, progress) 
        set 
            phase = newValue.first
            progress = newValue.second
        
    

    func path(in rect: CGRect) -> Path 
        var path = Path()

        let width = rect.width
        let height = rect.height
        let midWidth = width / 2
        let progressHeight = height * (1 - progress)

        path.move(to: CGPoint(x: 0, y: progressHeight))

        for x in stride(from: 0, to: width, by: 10) 
            let relativeX = x/waveLength
//            let normalizedLength = relativeX / midWidth

            let normalizedLength = (x - midWidth) / midWidth
            let y = progressHeight + sin(phase + relativeX) * amplitude * 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
    

【问题讨论】:

这是否回答了您的问题***.com/a/63412977/12299030? changeValueOverTime 可能会有所帮助。然后你可以使用changeValueOverTime(value: self.$progress, newValue: self.progress + 0.1, duration: 1) 而不是调用withAnimation self.progress += 0.1 @Asperi 这在更改进度值时不使用动画,因此并不能真正解决我的问题。 @vacawama 嗯,它并没有真正起作用,因为我的值在我的真实代码库中的 ViewModel 中被修改了 【参考方案1】:

至少目前(SwiftUI 2.0)没有添加(累积)SwiftUI 动画。所以这是可能的解决方法。

使用 Xcode 12 / ios 14 测试

struct WaterWaveView: View 
    @State var progress: CGFloat = 0.1
    @State var phase: CGFloat = 0.5

    var body: some View 
        VStack 
            WaterWave(progress: self.progress, phase: self.phase)
                .fill(Color.blue)
                .clipShape(Circle())
                .frame(width: 250, height: 250)
                .animation(phase == 0 ? .default : Animation.linear(duration: 1).repeatForever(autoreverses: false), value: phase)
                .animation(.easeOut(duration: 1), value: progress)
                .onAppear 
                            self.phase = .pi * 2
                
            Button("Add") 
                    self.phase = 0
                    self.progress += 0.1
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) 
                        self.phase = .pi * 2
                    
            
            Button("Reset") 
                self.progress = 0.0
            
        
    

【讨论】:

谢谢,它有效,因为在我真正的最终用例中,值是从 ViewModel 修改的将示例中的 Add 按钮操作的内容添加到 onChange :)

以上是关于使用 Button 触发的动画停止添加 onAppear 的 repeatForever 动画的主要内容,如果未能解决你的问题,请参考以下文章

怎么在Unity中实现这样的功能,关于按一直住Button时会触发事件,松开button后停止事件

使用 slideToggle 停止动画

animate() 在滚动停止之前不会触发

如何在 tableviewcell 按钮上停止动画?

按下“停止”按钮时如何停止动画并冻结图像

vue怎么给路由切换时添加动画