【SwiftUI】美しいカラーボールアニメーションの作り方

# はじめに
この記事では、SwiftUIで美しいカラーボールアニメーションを作る方法を紹介します。

# 作るもの
7色のカラーボールがなめらかに動くアニメーションを作ります。

# デモ
https://twitter.com/shota_appdev/status/1674409142990278657?s=61&t=qASBefFZzCAc6biMyBXZdA

# 作り方
## コード全体
```swift
import SwiftUI

struct AnimatedSevenGradientBallsView: View {
    private let colors: [Color]
    
    private let colorsCount: Int
    
    @State private var positions: [CGPoint]
    
    @State private var show = true
    
    init() {
        colors = [
            .red,
            .orange,
            .yellow,
            .green,
            .blue,
            .purple,
            .pink
        ]
        
        colorsCount = colors.count
        
        positions = Array(repeating: .zero, count: colorsCount)
    }
    
    var body: some View {
        ZStack {
            if show {
                ForEach(0..<colorsCount, id: \.self) { index in
                    Circle()
                        // Colorのgradientプロパティを使うと、
                        // その色のグラデーションを作れる
                        .fill(colors[index].gradient)

                        // blendModeをlightenにすると、
                        // 重なった部分が明るくなる
                        .blendMode(.lighten)

                        .frame(width: 160, height: 160)
                        .offset(x: positions[index].x, y: positions[index].y)
                        .animation(

                            // repeatForeverで繰り返しアニメーションを作れる
                            // autoreversesをtrueにすると、
                            // アニメーションが終わったら逆方向にアニメーションする
                            // delayでアニメーションの開始を遅らせることができる
                            // ここでは、indexに応じてアニメーションの開始を遅らせている
                            // そうすることで美しいアニメーションを実現している
                            .easeInOut(duration: 1).repeatForever(autoreverses: true).delay(Double(index) * 0.1),
                            value: positions
                        )
                }
            }
            
            Rectangle()

                // Materialを使うと、
                // 背後に表示されるビューにぼかしをかけられる
                .fill(Material.ultraThin)
                .ignoresSafeArea()
        }
        .preferredColorScheme(.dark) // 常にダークモードにする
        .onAppear {
            setPositions()
        }

        // iPhoneを回転させるとアニメーションが止まるバグがあるので、
        // リロードできるようにしている
        .onTapGesture {
            show.toggle()
        }
        .onChange(of: show) { show in
            if show {
                setPositions()
            } else {
                positions = Array(repeating: .zero, count: colorsCount)
            }
        }
    }
    
    private func setPositions() {
        // 角円の座標を円状に配置する（最初は全て中心にある）
        for index in 0 ..< colorsCount {
            positions[index] = CGPoint(
                x: 80 * cos(Double(index) * 2 * Double.pi / Double(colorsCount)),
                y: 80 * sin(Double(index) * 2 * Double.pi / Double(colorsCount))
            )
        }
    }
}
```

## ポイント
- blendModeをlightenにすると、重なった部分が明るくなる
- repeatForeverで繰り返しアニメーションを作れる
- autoreversesをtrueにすると、アニメーションが終わったら逆方向にアニメーションできる
- 最初は全て中心にある各円の座標を円状に配置する（この時にアニメーションがかかる）

# まとめ
SwiftUIで美しいカラーボールアニメーションを作りました。SwiftUIのテクニックが多く詰まっているので、自分でもまた見返したいと思いました👀