如何获得弹跳动画?

问题描述 投票:0回答:3

当应用程序需要关注并且在 Dock 上弹跳时,如何实现类似于 macOS Dock 上的弹跳动画。 SwiftUI 似乎只有缓和曲线和弹簧,这并没有真正强调反弹的方式。 我尝试了各种弹簧动画以及缓动曲线和计时曲线的组合来尝试让弹跳动画正常工作,但没有任何效果。

最接近弹跳动画的是插值弹簧,但这些动画的主要问题是它们在动画过程中超调,而弹跳动画则不会。

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)
    }
}

我正在寻找的是一个复制重力的弹跳动画,这是一个非常常见的动画,在很多游戏和软件中都可用

swift swiftui
3个回答
14
投票

什么是

.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")
            }
        }
    }
}

6
投票

使用多个偏移、延迟和缓动,我能够相当接近地复制该特定动画。

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
    }
}
}

0
投票

我一直在尝试为篮球制作弹跳动画。我遇到了几种选择,最终在观看 WWDC 2023 的视频后,我决定采用类似于 Josh Allen 的方法,但它结合了名为

phaseAnimator
的较新视图修改器的用户。

enter image description here

struct PhasedAnimations: View {
    @State private var trigger = false
    
    enum BouncePhase: CaseIterable {
        case initial, first, second, third, fourth
        
        var height: CGFloat {
            switch self {
            case .initial:
                return 0
            case .first:
                return 200
            case .second:
                return 150
            case .third:
                return 100
            case .fourth:
                return 50
            }
        }
        
        static var allCases: [PhasedAnimations.BouncePhase] {
            [initial, first, initial, second, initial, third, initial, fourth, initial]
        }
    }
    
    var body: some View {
        VStack {
            Image(systemName: "basketball.fill")
                .foregroundStyle(.orange)
                .background(Circle())
                .scaleEffect(4)
                .phaseAnimator(BouncePhase.allCases, trigger: trigger) { content, phase in
                    content.offset(y: phase.height * -1)
                } animation: { phase in
                    switch phase {
                    case .initial:
                        return .easeIn
                    default:
                        return .easeOut
                    }
                }
                .padding(.bottom)
                .padding(.bottom)
                .padding(.bottom)
            Button("Toggle") { trigger.toggle() }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.