Skip to content

Instantly share code, notes, and snippets.

@Koshimizu-Takehito
Last active July 22, 2023 03:34
Show Gist options
  • Save Koshimizu-Takehito/0325cc7c67e1b5bc867a12b1e72d5dfd to your computer and use it in GitHub Desktop.
Save Koshimizu-Takehito/0325cc7c67e1b5bc867a12b1e72d5dfd to your computer and use it in GitHub Desktop.
おしゃれなローディングアニメーション
// Thanks to @Ren_yello
// https://twitter.com/ren_yello/status/1681856952090112000?s=61&t=SNv1ZCU_S3Y8TobPv4MBww
import SwiftUI
struct PhisicsLoadingView: View {
@State private var color: CGColor = .black
@State private var angle1: CGFloat = .pi/2
@State private var angle2: CGFloat = .pi/2
@State private var angle3: CGFloat = .pi/2
var body: some View {
ZStack {
LoadingCircle(angle: angle1, color: color)
LoadingCircle(angle: angle2, color: color)
LoadingCircle(angle: angle3, color: color)
}
.padding()
.task {
await updateColor()
}
.task {
let animation = timingAnimation
.repeatForever(autoreverses: false)
withAnimation(animation) { angle1 += 2 * .pi }
try? await Task.sleep(nanoseconds: 400_000_000)
withAnimation(animation) { angle2 += 2 * .pi }
try? await Task.sleep(nanoseconds: 400_000_000)
withAnimation(animation) { angle3 += 2 * .pi }
}
}
func updateColor() async {
withAnimation(timingAnimation) {
color = .random
}
try? await Task.sleep(nanoseconds: 3_000_000_000)
Task { await updateColor() }
}
var timingAnimation: Animation {
let start = UnitPoint(x: 0.1, y: 0.7)
let end = UnitPoint(x: 0.9, y: 0.3)
let bezier = UnitCurve.bezier(startControlPoint: start, endControlPoint: end)
return .timingCurve(bezier, duration: 3)
}
}
private struct LoadingCircle: View, Animatable {
var angle: CGFloat
var color: CGColor
var animatableData: CGFloat { get { angle } set { angle = newValue } }
var body: some View {
GeometryReader { geometory in
let width = 0.1 * min(geometory.size.width, geometory.size.height)
let x = (geometory.size.width - width) / 2
let y = (geometory.size.height - width) / 2
let r = min(x, y)
Circle().frame(width: width)
.offset(x: x, y: y)
.offset(x: r * cos(angle), y: r * sin(angle))
.foregroundStyle(Color(cgColor: color))
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
extension CGColor {
class var black: Self {
self.init(red: 0, green: 0, blue: 0, alpha: 1)
}
class var random: Self {
var value: CGFloat { .random(in: 0...1) }
return self.init(red: value, green: value, blue: value, alpha: 1)
}
}
struct PhisicsLoadingView_Previews: PreviewProvider {
static var previews: some View {
PhisicsLoadingView()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment