Created
October 27, 2023 14:39
-
-
Save iAmVishal16/b4bb22e20f68f923470d29702e210e39 to your computer and use it in GitHub Desktop.
Rainbow Spiral in SwiftUI. Use apple Playground app for best result.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // Spiral.swift | |
| // ShapeAnimation | |
| // | |
| // Created by Vishal Paliwal on 27/06/23. | |
| // | |
| import SwiftUI | |
| struct SpiralView: View { | |
| @State private var animate = false | |
| @State private var angle: Double = 360.0 | |
| var body: some View { | |
| ZStack { | |
| Color.black | |
| .ignoresSafeArea(edges: .all) | |
| ForEach(0..<20, id: \.self) { index in | |
| Spiral(circleCount: 64, circleRadius: 5, circleSpacing: Double(index) * 10) | |
| .rotationEffect(.degrees(Double(index) * 5), anchor: .center) | |
| .rotationEffect(.degrees(animate ? angle : -360), anchor: .center) | |
| .hueRotation(Angle(degrees: animate ? angle : 0)) | |
| .animation(.easeInOut(duration: 2.5).delay(0.1 * Double(index)), value: animate) | |
| } | |
| } | |
| .onChange(of: animate, perform: { value in | |
| DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) { | |
| withAnimation(Animation.easeOut(duration: 2.5).repeatForever(autoreverses: true)) { | |
| self.animate.toggle() | |
| } | |
| } | |
| }) | |
| .onAppear(perform: { | |
| DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) { | |
| animate.toggle() | |
| } | |
| }) | |
| } | |
| } | |
| struct Spiral: View { | |
| @State private var animate = false | |
| let circleCount: Int // Number of circles to display | |
| let circleRadius: CGFloat // Radius of each circle | |
| let circleSpacing: CGFloat // Spacing between circles | |
| init(circleCount: Int, circleRadius: CGFloat, circleSpacing: CGFloat) { | |
| self.circleCount = circleCount | |
| self.circleRadius = circleRadius | |
| self.circleSpacing = circleSpacing | |
| } | |
| var body: some View { | |
| GeometryReader(content: { geometry in | |
| ZStack { | |
| ForEach(0..<circleCount, id: \.self) { index in | |
| let angle = 2 * .pi / Double(circleCount) * Double(index) | |
| let xOffset = cos(angle) * Double(circleRadius + circleSpacing) | |
| let yOffset = sin(angle) * Double(circleRadius + circleSpacing) | |
| Circle() | |
| .foregroundColor(.yellow) | |
| .frame(width: Double(index) * 0.03 * circleRadius) | |
| .position(x: geometry.size.width / 2 + CGFloat(xOffset), | |
| y: geometry.size.height / 2 + CGFloat(yOffset)) | |
| .opacity(0.1 * Double(index)) | |
| .rainbow() | |
| } | |
| } | |
| }) | |
| } | |
| } | |
| struct Spiral_Previews: PreviewProvider { | |
| static var previews: some View { | |
| SpiralView() | |
| .preferredColorScheme(.dark) | |
| } | |
| } | |
| struct Rainbow: ViewModifier { | |
| let hueColors = stride(from: 0, to: 1, by: 0.01).map { | |
| Color(hue: $0, saturation: 1, brightness: 1) | |
| } | |
| func body(content: Content) -> some View { | |
| content | |
| .overlay(GeometryReader { (proxy: GeometryProxy) in | |
| ZStack { | |
| LinearGradient(gradient: Gradient(colors: self.hueColors), | |
| startPoint: .leading, | |
| endPoint: .trailing) | |
| .frame(width: proxy.size.width) | |
| } | |
| }) | |
| .mask(content) | |
| } | |
| } | |
| extension View { | |
| func rainbow() -> some View { | |
| self.modifier(Rainbow()) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment