Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ynagatomo/9b1e1c17b5b6cbf8d0bdb0e6ca06b739 to your computer and use it in GitHub Desktop.
Save ynagatomo/9b1e1c17b5b6cbf8d0bdb0e6ca06b739 to your computer and use it in GitHub Desktop.
// Modified TAATHub/GradientBorderAnimation.swift on 2025/03/01, Y.Nagatomo
// https://gist.github.com/TAATHub/0d312611e0b31265f85d3c555bf43039
// Gradient Border Animation
// See Also: https://x.com/sucodeee/status/1894700908824395843
import SwiftUI
struct ContentView: View {
@State var rotation: CGFloat = 0
var body: some View {
HStack(spacing: 40) {
RoundedRectangle(cornerRadius: 20, style: .continuous)
.frame(width: 200, height: 100)
.foregroundStyle(.black)
.overlay {
caption("RoundedRectangle")
}
.gradientBorder(shape: RoundedRectangle(cornerRadius: 20, style: .continuous),
size: .init(width: 200, height: 100),
color: .red)
UnevenRoundedRectangle(cornerRadii: .init(topLeading: 50, bottomTrailing: 50))
.frame(width: 200, height: 100)
.foregroundStyle(.black)
.overlay {
caption("UnevenRoundedRectangle")
}
.gradientBorder(shape: UnevenRoundedRectangle(cornerRadii: .init(topLeading: 50, bottomTrailing: 50)),
size: .init(width: 200, height: 100),
color: .green)
Capsule()
.frame(width: 75, height: 150)
.overlay {
caption("Capsule")
}
.gradientBorder(shape: Capsule(),
size: .init(width: 75, height: 150),
color: .blue)
Ellipse()
.frame(width: 150, height: 100)
.overlay {
caption("Ellipse")
}
.gradientBorder(shape: Ellipse(),
size: .init(width: 150, height: 100),
color: .yellow)
Circle()
.frame(width: 100, height: 100)
.overlay {
caption("Circle")
}
.gradientBorder(shape: Circle(),
size: .init(width: 100, height: 100),
color: .purple)
}
.padding(.vertical, 100)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.black.opacity(0.8))
}
private func caption(_ text: String) -> some View {
Text(text)
.bold()
.font(.caption)
.foregroundStyle(.white)
}
}
#Preview {
ContentView()
}
struct GradientBorderModifier<S: Shape>: ViewModifier {
@State var rotation: CGFloat = 0
let shape: S
let size: CGSize
let color: Color
private var gradientSize: CGFloat {
(size.width > size.height ? size.width : size.height) * 1.2
}
private var startPoint: UnitPoint {
size.width > size.height ? .top : .leading
}
private var endPoint: UnitPoint {
size.width > size.height ? .bottom : .trailing
}
private var colors: [Color] {
[.clear, color.opacity(0.01), color, .white, color, color.opacity(0.01), .clear]
}
func body(content: Content) -> some View {
content
.overlay {
Rectangle()
.frame(width: gradientSize, height: gradientSize)
.foregroundStyle(LinearGradient(gradient: .init(colors: colors), startPoint: startPoint, endPoint: endPoint))
.rotationEffect(.degrees(rotation))
.mask {
shape
.stroke(lineWidth: 4)
.frame(width: size.width, height: size.height - 4)
}
}
.onAppear {
withAnimation(.linear(duration: 4).repeatForever(autoreverses: false)) {
rotation = 360
}
}
}
}
extension View {
func gradientBorder<S: Shape>(shape: S, size: CGSize, color: Color) -> some View {
modifier(GradientBorderModifier(shape: shape, size: size, color: color))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment