如何在SwiftUI中实现弹跳动画?

3
我该如何实现类似于macOS dock上应用程序需要注意时弹跳的弹跳动画。SwiftUI似乎只有缓动曲线和弹簧,这些都不能像弹跳一样突出显示。 我已经尝试了各种弹簧动画以及缓动曲线和timingCurves的组合,但没有真正做到弹跳动画的效果。 最接近弹跳动画的是插值弹簧,但这些动画的主要问题在于它们在动画过程中会超调,而弹跳动画则不会。
struct ContentView: View {
    @State var bounce = false
    @State private var initialVelocity:Double = 1
    @State private var damping:Double = 1
    @State private var stiffness:Double = 1
    var body: some View {
        VStack {
            Circle().fill(Color.red).frame(width:50,height:50)
            .offset(y: bounce ? 0 : -80)
                .animation(.interpolatingSpring(stiffness: self.stiffness, damping: self.damping, initialVelocity: self.initialVelocity))

            HStack(){
                Text("stiffness")
                Slider(value: $stiffness, in: 0...100)
            }
            HStack(){
                Text("damping")
                Slider(value: $damping, in: 0...100)
            }
            HStack(){
                Text("initialVelocity")
                Slider(value: $initialVelocity, in: 0...100)
            }
            Button("Animate" ){
                self.bounce.toggle()
            }
        }.padding(.horizontal)
    }
}

我需要的是一种能够模拟重力的反弹动画,这是一种非常常见的动画效果,在许多游戏和软件中都可以找到。 enter image description here
2个回答

11

.interpolatingSpring(...) 是什么?
考虑以下示例,但请记住您可能需要尝试不同的 stiffness 等值:

struct ContentView: View {
    @State var test = false
    
    var body: some View {
        VStack {
            Text("Animatable")
            .offset(y: test ? 0 : -80)
            .animation(.interpolatingSpring(stiffness: 350, damping: 5, initialVelocity: 10))
            
            Button(action: {self.test.toggle()}) {
                Text("Animate")
            }
        }
    }
}

谢谢你的建议,但我已经尝试过很多次了,主要问题是动画超出了预期。而反弹动画模拟了重力,并在底部完全停止弹跳。有没有办法在SwiftUI中实现这个效果?我相当确定在UIKit中是可以实现的,或者至少有无数的库可以做到。 - Anthney
老实说,我不确定在SwiftUI中是否有办法实现这个。但是你尝试过interpolatingSpring(...)的其他参数了吗?完整声明如下:static func interpolatingSpring(mass: Double = 1.0, stiffness: Double, damping: Double, initialVelocity: Double = 0.0) -> Animation - finebel
答案与问题所问的完全不同。另一个问题是,您必须手动更改变量的状态,以将目标恢复到原始位置。 - Farid

3

通过使用多个偏移量、延迟和缓动效果,我能够相当接近复制那个特定的动画。

演示

struct ContentView: View {

@State var bounceHeight: BounceHeight? = nil

func bounceAnimation() {
    withAnimation(Animation.easeOut(duration: 0.3).delay(0)) {
        bounceHeight = .up100
    }
    withAnimation(Animation.easeInOut(duration: 0.04).delay(0)) {
        bounceHeight = .up100
    }
    withAnimation(Animation.easeIn(duration: 0.3).delay(0.34)) {
        bounceHeight = .base
    }
    withAnimation(Animation.easeOut(duration: 0.2).delay(0.64)) {
        bounceHeight = .up40
    }
    withAnimation(Animation.easeIn(duration: 0.2).delay(0.84)) {
        bounceHeight = .base
    }
    withAnimation(Animation.easeOut(duration: 0.1).delay(1.04)) {
        bounceHeight = .up10
    }
    withAnimation(Animation.easeIn(duration: 0.1).delay(1.14)) {
        bounceHeight = .none
    }
}

var body: some View {
    VStack {
        Text("☝️")
            .font(.system(size: 200))
            .multilineTextAlignment(.center)
            .minimumScaleFactor(0.2)
            .lineLimit(1)
    }
    .padding(8)
    .frame(width: 72, height: 72)
    .background(.purple.opacity(0.4))
    .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
    .shadow(radius: 3)
    .overlay(
        RoundedRectangle(cornerRadius: 16)
            .stroke(.purple, lineWidth: 2)
    )
    .offset(y: bounceHeight?.associatedOffset ?? 0)
    .onTapGesture {
        bounceAnimation()
    }
}
}

enum BounceHeight {
case up100, up40, up10, base
var associatedOffset: Double {
    switch self {
    case .up100:
        return -100
    case .up40:
        return -40
    case .up10:
        return -10
    case .base:
        return 0
    }
}
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接