如何在SwiftUI中实现类似UIKit的帧动画(仿魂斗罗像素风波动)
SwiftUI 实现魂斗罗风格重复波动动画序列
核心解决方案:封装可复用的动画修饰符
针对你的需求,我们可以通过**自定义ViewModifier**封装完整的动画序列逻辑,既避免在onAppear中硬编码定时器,又能让任意视图快速应用该动画,完全符合SwiftUI的声明式设计理念。
完整实现代码
1. 自定义动画修饰符
import SwiftUI struct ContraWaveAnimation: ViewModifier { @State private var offsetY: CGFloat = 0 // 可调整的动画参数 private let singleMoveDistance: CGFloat = 10 private let animationSpeed: Double = 0.2 private let pauseDuration: Double = 0.3 func body(content: Content) -> some View { content .offset(y: offsetY) .task { // 无限循环执行动画序列 while true { // 步骤1:上移 withAnimation(.linear(duration: animationSpeed)) { offsetY -= singleMoveDistance } // 步骤2:暂停 try? await Task.sleep(nanoseconds: UInt64(pauseDuration * 1e9)) // 步骤3:上移 withAnimation(.linear(duration: animationSpeed)) { offsetY -= singleMoveDistance } // 步骤4:暂停 try? await Task.sleep(nanoseconds: UInt64(pauseDuration * 1e9)) // 步骤5:下移 withAnimation(.linear(duration: animationSpeed)) { offsetY += singleMoveDistance } // 步骤6:暂停 try? await Task.sleep(nanoseconds: UInt64(pauseDuration * 1e9)) // 步骤7:下移 withAnimation(.linear(duration: animationSpeed)) { offsetY += singleMoveDistance } // 步骤8:暂停 try? await Task.sleep(nanoseconds: UInt64(pauseDuration * 1e9)) } } } }
2. 扩展View简化调用
extension View { func contraWaveAnimation() -> some View { self.modifier(ContraWaveAnimation()) } }
3. 任意视图快速应用动画
struct ContentView: View { var body: some View { VStack(spacing: 50) { // 给文本应用动画 Text("❤️") .font(.system(size: 200, weight: .bold)) .contraWaveAnimation() // 给自定义视图应用动画 CustomRocketView() .contraWaveAnimation() } } } // 自定义视图示例 struct CustomRocketView: View { var body: some View { Image(systemName: "rocket") .font(.system(size: 100)) .foregroundColor(.orange) } }
方案优势说明
- 脱离onAppear硬编码:使用SwiftUI的
.task修饰符管理异步动画序列,视图出现时自动启动,消失时自动终止,完全贴合视图生命周期,避免内存泄漏。 - 任意视图复用:通过
ViewModifier封装动画逻辑,任何视图只需调用.contraWaveAnimation()即可应用效果,无需直接操作视图实例。 - 参数灵活调整:可以通过修改
singleMoveDistance、animationSpeed、pauseDuration参数,快速调整动画的幅度、速度和停顿间隔。 - 像素风优化(可选):如果需要更贴近魂斗罗的像素波动效果,可以在动画步骤中加入微小的X轴偏移,比如:
@State private var offsetX: CGFloat = 0 // 在上移动画中添加: offsetX += 2 // 在下移动画中添加: offsetX -= 2
内容的提问来源于stack exchange,提问作者user23194465
相关产品推荐
相关产品推荐

