Skip to content

Instantly share code, notes, and snippets.

@renyello
Created July 20, 2023 06:10
Show Gist options
  • Save renyello/862a0626e5c2ade40ed3364e2cd7acaa to your computer and use it in GitHub Desktop.
Save renyello/862a0626e5c2ade40ed3364e2cd7acaa to your computer and use it in GitHub Desktop.
四つの小さな円が正方形の角をサイズを変えながら移動します。
import SwiftUI
struct ProgressRectangle: View {
@State private var index = [0, 1, 2, 3]
@State private var scale: [CGFloat] = [1.0, 0.4, 1.0, 0.4] // Scale for each circle
let colors = [Color("Red"), Color("Green"), Color("Blue"), Color("Yellow")] // Colors for each circle
let minScale: CGFloat = 1.0 // Minimum scale value
let maxScale: CGFloat = 2.0 // Maximum scale value
@State private var opacity = 1.0
@State private var percentage: Int = 0 // Progress percentage
let timer = Timer.publish(every: 0.5, on: .main, in: .common).autoconnect() // Timer to update the percentage
let timer2 = Timer.publish(every: 0.3, on: .main, in: .common).autoconnect()
@State private var shouldAnimate = true // Control animation
var body: some View {
ZStack{
Color("Beige")
.ignoresSafeArea()
GeometryReader { geometry in
let dimension = min(geometry.size.width, geometry.size.height) * 0.4 // 30% of the minimum dimension
let smallSquareSize = dimension / 5
let offsetX = (geometry.size.width - dimension) / 2
let offsetY = (geometry.size.height - dimension) / 2
let positions = [
CGPoint(x: offsetX + smallSquareSize / 2, y: offsetY + smallSquareSize / 2),
CGPoint(x: offsetX + dimension - smallSquareSize / 2, y: offsetY + smallSquareSize / 2),
CGPoint(x: offsetX + dimension - smallSquareSize / 2, y: offsetY + dimension - smallSquareSize / 2),
CGPoint(x: offsetX + smallSquareSize / 2, y: offsetY + dimension - smallSquareSize / 2)
]
ZStack { // Set the origin at top leading
ForEach(0..<4) { i in
// Small Circles
Circle()
.fill(colors[i]) // Assigning color from the colors array
.opacity(opacity)
.frame(width: smallSquareSize, height: smallSquareSize)
.scaleEffect(scale[i]) // Apply scale effect
.position(positions[index[i]])
.animation(shouldAnimate ? .spring(response: 1, dampingFraction: 1, blendDuration: 1) : .default, value: index[i]) // Control animation with shouldAnimate
}
Text("\(percentage)%")
.font(.system(size: 25)) // smaller font size
.opacity(opacity)
.foregroundColor(Color.gray)
.onReceive(timer2) { _ in
if percentage < 100 {
percentage += Int.random(in: 1...5)
if percentage >= 100 {
percentage = 100
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
withAnimation(.linear(duration: 0.4)) {
self.opacity = 0
}
}
}
}
}
}
.onAppear {
// Trigger the timer manually for the first time
index = index.map { ($0 + 1) % positions.count }
scale = scale.indices.map { i in
if (i == 0 || i == 2) {
return scale[i] == minScale && percentage < 100 ? maxScale : minScale
} else {
return scale[i] == minScale && percentage < 100 ? maxScale : minScale
}
}
// Then schedule the timer to fire periodicall
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
if shouldAnimate { // Only animate when shouldAnimate is true
index = index.map { ($0 + 1) % positions.count }
// Update scale individually for each circle
scale = scale.indices.map { i in
if (i == 0 || i == 2) {
return scale[i] == minScale && percentage < 100 ? maxScale : minScale
} else {
return scale[i] == minScale && percentage < 100 ? maxScale : minScale
}
}
}
}
}
}
.padding() // Padding applied here
}
}
}
struct ProgressRectangle_Previews: PreviewProvider {
static var previews: some View {
ProgressRectangle()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment