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

SwiftUI中自定义Shape波浪动画无法重新启动的问题

解决波浪动画无法重启的问题

问题原因

第一次调用animateWave后,phase被设置为π*2,再次调用时目标值与当前值完全相同,SwiftUI不会触发新的动画;同时停止动画时未重置phase状态,导致后续启动时没有可驱动动画的状态变化。

解决方案

  1. 停止动画时通过withAnimation(nil)取消当前的phase动画,并重置phase为初始值,确保下次启动时有明确的状态差
  2. 修改animateWave函数,让phase每次启动时累加π*2而非设置固定值,保证每次调用都能触发动画

修改后的完整代码

struct Wave: Shape {
    var strength: Double
    var frequency: Double
    var phase: Double
    var animatableData: AnimatablePair<Double, Double> {
        get { AnimatablePair(phase, strength) }
        set {
            self.phase = newValue.first
            self.strength = newValue.second
        }
    }
    
    func path(in rect: CGRect) -> Path {
        var path = Path()

        let width = Double(rect.width)
        let height = Double(rect.height)
        let midHeight = height / 2
        let wavelength = width / frequency

        let firstX = 0.0
        let firstRelativeX = firstX / wavelength
        let firstSine = sin(firstRelativeX + phase)
        let firstY = strength * firstSine + midHeight
        path.move(to: CGPoint(x: firstX, y: firstY))

        for x in stride(from: 0.0, through: width, by: 1) {
            let relativeX = x / wavelength
            let sine = sin(relativeX + phase)
            let y = strength * sine + midHeight
            path.addLine(to: CGPoint(x: x, y: y))
        }

        return path
    }
}

struct WaveView: View {
    @Binding var isAnimating: Bool
    @State private var phase = 0.0
    @State private var waveStrength = 50.0
    
    var body: some View {
        VStack {
            Wave(strength: waveStrength, frequency: 30, phase: phase)
                .stroke(.black, lineWidth: 5)
                .onChange(of: isAnimating) { _, newValue in
                    withAnimation(.easeInOut(duration: 0.5)) {
                        waveStrength = newValue ? 50.0 : 0.0
                    }
                    if newValue {
                        animateWave()
                    } else {
                        // 取消动画并重置phase到初始状态
                        withAnimation(nil) {
                            phase = 0.0
                        }
                    }
                }
                .onAppear {
                    animateWave()
                }
        }
    }
    
    private func animateWave() {
        withAnimation(Animation.linear(duration: 2).repeatForever(autoreverses: false)) {
            // 每次启动时累加phase,确保状态变化触发动画
            phase += .pi * 2
        }
    }
}

struct WaveContainerView: View {
    @State var isAnimating = true

    var body: some View {
        VStack {
            WaveView(isAnimating: $isAnimating)
            
            Button("Animate") {
                isAnimating.toggle()
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 07:32:13