SwiftUI中自定义Shape波浪动画无法重新启动的问题
解决波浪动画无法重启的问题
问题原因
第一次调用animateWave后,phase被设置为π*2,再次调用时目标值与当前值完全相同,SwiftUI不会触发新的动画;同时停止动画时未重置phase状态,导致后续启动时没有可驱动动画的状态变化。
解决方案
- 停止动画时通过
withAnimation(nil)取消当前的phase动画,并重置phase为初始值,确保下次启动时有明确的状态差 - 修改
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
相关产品推荐
相关产品推荐

