はじめに
Pinterestを起動すると、最初にロゴが楽しげに動くアニメーションが表示されます。
今回は、このアニメーションを再現します。
デモ
型を定義
まず、Viewがある状態に変化するまでにかかる時間、ある状態でのサイズと位置をまとめたAnimationStep
を定義します。
struct AnimationStep {
let duration: Double // アニメーションの継続時間
let diameter: CGFloat // ロゴの直径
let yOffset: CGFloat // y軸方向のオフセット
init(
duration: Double,
diameter: CGFloat = 100,
yOffset: CGFloat = 0
) {
self.duration = duration
self.diameter = diameter
self.yOffset = yOffset
}
}
アニメーションを配列で定義
次に、全体のアニメーションを配列で定義します。
animationSteps
プロパティに注目して下さい。
最初に少し小さくなって、大きくなって、元のサイズに戻るアニメーションがあり、
その後少し下に移動して、今度は上に消えていくアニメーションがあります。
import SwiftUI
struct ContentView: View {
@State private var diameter: CGFloat = 100
@State private var yOffset: CGFloat = 0
private let animationSteps: [AnimationStep] = [
AnimationStep(duration: 0.1, diameter: 80),
AnimationStep(duration: 0.2, diameter: 120),
AnimationStep(duration: 0.1, diameter: 100),
AnimationStep(duration: 0.3, yOffset: 150),
AnimationStep(duration: 0.6, yOffset: -600),
]
var body: some View {
Image(systemName: "pencil.circle.fill")
.resizable()
.foregroundStyle(.red)
.frame(width: diameter, height: diameter)
.offset(y: yOffset)
}
}
アニメーションを再生する
最後に、作ったアニメーションを再生します。
onAppear
内の処理に注目して下さい。
animationSteps.enumerated()
で取得したanimationSteps
のインデックスと要素をfor文で回しています。
withAnimation
で指定しているアニメーションは、step
のduration
分継続し、それまでのstep
のduration
の合計分遅延させています。
こうすることにより、step
のアニメーションが終わってから次のstep
のアニメーションが始まるようになります。
struct ContentView: View {
@State private var diameter: CGFloat = 100
@State private var yOffset: CGFloat = 0
private let animationSteps: [AnimationStep] = [
AnimationStep(duration: 0.1, diameter: 80),
AnimationStep(duration: 0.2, diameter: 120),
AnimationStep(duration: 0.1, diameter: 100),
AnimationStep(duration: 0.3, yOffset: 150),
AnimationStep(duration: 0.6, yOffset: -600),
]
var body: some View {
Image(systemName: "pencil.circle.fill")
.resizable()
.foregroundStyle(.red)
.frame(width: diameter, height: diameter)
.offset(y: yOffset)
.onAppear {
for (index, step) in animationSteps.enumerated() {
withAnimation(
.easeInOut(duration: step.duration)
.delay(index > 0 ? animationSteps[0..<index].reduce(0) { $0 + $1.duration } : 0)
) {
diameter = step.diameter
yOffset = step.yOffset
}
}
}
}
}
おわりに
今回は、理解しやすいプログラムと複雑なアニメーションを両立させることができました。この記事が参考になったという方は、いいねとフォローよろしくお願いします☺️