HuggingChat-Mac/Animations/FluidGradient/FluidGradient.swift (105 lines of code) (raw):
//
// FluidGradient.swift
// FluidGradient
//
// Created by Oskar Groth on 2021-12-23.
//
import SwiftUI
public struct FluidGradient: View {
private var blobs: [Color]
private var highlights: [Color]
private var speed: CGFloat
private var blur: CGFloat
@State var blurValue: CGFloat = 0.0
public init(blobs: [Color],
highlights: [Color] = [],
speed: CGFloat = 1.0,
blur: CGFloat = 0.75) {
self.blobs = blobs
self.highlights = highlights
self.speed = speed
self.blur = blur
}
public var body: some View {
Representable(blobs: blobs,
highlights: highlights,
speed: speed,
blurValue: $blurValue)
.blur(radius: pow(blurValue, blur))
.accessibility(hidden: true)
.clipped()
}
}
#if os(OSX)
typealias SystemRepresentable = NSViewRepresentable
#else
typealias SystemRepresentable = UIViewRepresentable
#endif
// MARK: - Representable
extension FluidGradient {
struct Representable: SystemRepresentable {
var blobs: [Color]
var highlights: [Color]
var speed: CGFloat
@Binding var blurValue: CGFloat
func makeView(context: Context) -> FluidGradientView {
context.coordinator.view
}
func updateView(_ view: FluidGradientView, context: Context) {
context.coordinator.create(blobs: blobs, highlights: highlights)
DispatchQueue.main.async {
context.coordinator.update(speed: speed)
}
}
#if os(OSX)
func makeNSView(context: Context) -> FluidGradientView {
makeView(context: context)
}
func updateNSView(_ view: FluidGradientView, context: Context) {
updateView(view, context: context)
}
#else
func makeUIView(context: Context) -> FluidGradientView {
makeView(context: context)
}
func updateUIView(_ view: FluidGradientView, context: Context) {
updateView(view, context: context)
}
#endif
func makeCoordinator() -> Coordinator {
Coordinator(blobs: blobs,
highlights: highlights,
speed: speed,
blurValue: $blurValue)
}
}
class Coordinator: FluidGradientDelegate {
var blobs: [Color]
var highlights: [Color]
var speed: CGFloat
var blurValue: Binding<CGFloat>
var view: FluidGradientView
init(blobs: [Color],
highlights: [Color],
speed: CGFloat,
blurValue: Binding<CGFloat>) {
self.blobs = blobs
self.highlights = highlights
self.speed = speed
self.blurValue = blurValue
self.view = FluidGradientView(blobs: blobs,
highlights: highlights,
speed: speed)
self.view.delegate = self
}
/// Create blobs and highlights
func create(blobs: [Color], highlights: [Color]) {
guard blobs != self.blobs || highlights != self.highlights else { return }
self.blobs = blobs
self.highlights = highlights
view.create(blobs, layer: view.baseLayer)
view.create(highlights, layer: view.highlightLayer)
view.update(speed: speed)
}
/// Update speed
func update(speed: CGFloat) {
guard speed != self.speed else { return }
self.speed = speed
view.update(speed: speed)
}
func updateBlur(_ value: CGFloat) {
blurValue.wrappedValue = value
}
}
}