当应用程序需要关注并且在 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)
}
}
什么是
.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")
}
}
}
}
使用多个偏移、延迟和缓动,我能够相当接近地复制该特定动画。
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
}
}
}
我一直在尝试为篮球制作弹跳动画。我遇到了几种选择,最终在观看 WWDC 2023 的视频后,我决定采用类似于 Josh Allen 的方法,但它结合了名为
phaseAnimator
的较新视图修改器的用户。
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() }
}
}
}