Skip to content

Instantly share code, notes, and snippets.

@MoussaHellal
Created January 24, 2024 11:22
Show Gist options
  • Save MoussaHellal/cc374eb61c76875813414cfb6554aca7 to your computer and use it in GitHub Desktop.
Save MoussaHellal/cc374eb61c76875813414cfb6554aca7 to your computer and use it in GitHub Desktop.
Provides a Wave Animation
import SwiftUI
struct ContentView: View {
/// The percentage value controlling the wave animation.
@State private var percent: CGFloat = 60.0
/// The linear gradient defining the color of the wave.
@State private var color = LinearGradient(colors: [.cyan, .cyan.opacity(0.5)], startPoint: .top, endPoint: .bottom)
var body: some View {
VStack {
CircleWaveView(percent: 50, color: LinearGradient(colors: [.cyan, .cyan.opacity(0.5)], startPoint: .top, endPoint: .bottom))
CircleWaveView(percent: 75, color: LinearGradient(colors: [.red, .yellow , .cyan.opacity(0.5)], startPoint: .top, endPoint: .bottom))
CircleWaveView(percent: 75, color: LinearGradient(colors: [.cyan, .blue.opacity(0.5)], startPoint: .top, endPoint: .bottom))
CircleWaveView(percent: 50, color: LinearGradient(colors: [.blue, .blue.opacity(0.7)], startPoint: .top, endPoint: .bottom))
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.white)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
VStack {
CircleWaveView(percent: 58, color: LinearGradient(colors: [.cyan, .cyan.opacity(0.5)], startPoint: .top, endPoint: .bottom))
CircleWaveView(percent: 80, color: LinearGradient(colors: [.red, .white,.cyan.opacity(0.5)], startPoint: .top, endPoint: .bottom))
CircleWaveView(percent: 30, color: LinearGradient(colors: [.cyan, .blue.opacity(0.5)], startPoint: .top, endPoint: .bottom))
}
.background(.gray.opacity(0.2))
.frame(maxHeight: .infinity)
}
}
/// A custom shape representing a wave.
struct Wave: Shape {
/// The angle offset for the wave animation.
var offset: Angle
/// The percentage value controlling the shape of the wave.
var percent: Double
/// The animatable data for the wave animation.
var animatableData: Double {
get { offset.degrees }
set { offset = Angle(degrees: newValue) }
}
/// Creates a path for the wave shape within the specified rectangle.
func path(in rect: CGRect) -> Path {
var path = Path()
let lowestWave = 0.02
let highestWave = 0.98
let newPercent = lowestWave + (highestWave - lowestWave) * percent
let waveHeight = 0.015 * rect.height
let yoffset = CGFloat(1 - newPercent) * (rect.height - 4 * waveHeight) + 2 * waveHeight
let startAngle = offset
let endAngle = offset + Angle(degrees: 360)
path.move(to: CGPoint(x: 0, y: yoffset + waveHeight * CGFloat(sin(offset.radians))))
for angle in stride(from: startAngle.degrees, through: endAngle.degrees, by: 5) {
let x = CGFloat((angle - startAngle.degrees) / 360) * rect.width
path.addLine(to: CGPoint(x: x, y: yoffset + waveHeight * CGFloat(sin(Angle(degrees: angle).radians))))
}
path.addLine(to: CGPoint(x: rect.width, y: rect.height))
path.addLine(to: CGPoint(x: 0, y: rect.height))
path.closeSubpath()
return path
}
}
/// A SwiftUI view displaying a circular wave animation.
struct CircleWaveView: View {
/// The angle offset for the wave animation.
@State private var waveOffset = Angle(degrees: 0)
/// The percentage value controlling the shape of the wave.
let percent: CGFloat
/// The linear gradient defining the color of the wave.
let color: LinearGradient
var body: some View {
GeometryReader { geo in
ZStack {
Circle()
.overlay(
Wave(offset: Angle(degrees: self.waveOffset.degrees), percent: Double(percent)/100)
.fill(color)
.clipShape(Circle().scale(0.92))
)
.background(.white)
}
}
.aspectRatio(1, contentMode: .fit)
.onAppear {
withAnimation(Animation.linear(duration: 2).repeatForever(autoreverses: false)) {
self.waveOffset = Angle(degrees: 360)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment