HuggingChat-Mac/Animations/FluidGradient/BlobLayer.swift (70 lines of code) (raw):

// // BlobLayer.swift // BlobLayer // // Created by João Gabriel Pozzobon dos Santos on 04/10/22. // import SwiftUI /// A CALayer that draws a single blob on the screen public class BlobLayer: CAGradientLayer { init(color: Color) { super.init() self.type = .radial #if os(OSX) autoresizingMask = [.layerWidthSizable, .layerHeightSizable] #endif // Set color set(color: color) // Center point let position = newPosition() self.startPoint = position // Radius let radius = newRadius() self.endPoint = position.displace(by: radius) } /// Generate a random point on the canvas func newPosition() -> CGPoint { return CGPoint(x: CGFloat.random(in: 0.0...1.0), y: CGFloat.random(in: 0.0...1.0)).capped() } /// Generate a random radius for the blob func newRadius() -> CGPoint { let size = CGFloat.random(in: 0.15...0.75) let viewRatio = frame.width/frame.height let safeRatio = max(viewRatio.isNaN ? 1 : viewRatio, 1) let ratio = safeRatio*CGFloat.random(in: 0.25...1.75) return CGPoint(x: size, y: size*ratio) } /// Animate the blob to a random point and size on screen at set speed func animate(speed: CGFloat) { guard speed > 0 else { return } self.removeAllAnimations() let currentLayer = self.presentation() ?? self let animation = CASpringAnimation() animation.mass = 10/speed animation.damping = 50 animation.duration = 1/speed animation.isRemovedOnCompletion = false animation.fillMode = CAMediaTimingFillMode.forwards let position = newPosition() let radius = newRadius() // Center point let start = animation.copy() as! CASpringAnimation start.keyPath = "startPoint" start.fromValue = currentLayer.startPoint start.toValue = position // Radius let end = animation.copy() as! CASpringAnimation end.keyPath = "endPoint" end.fromValue = currentLayer.endPoint end.toValue = position.displace(by: radius) self.startPoint = position self.endPoint = position.displace(by: radius) // Opacity let value = Float.random(in: 0.5...1) let opacity = animation.copy() as! CASpringAnimation opacity.fromValue = self.opacity opacity.toValue = value self.opacity = value self.add(opacity, forKey: "opacity") self.add(start, forKey: "startPoint") self.add(end, forKey: "endPoint") } /// Set the color of the blob func set(color: Color) { // Converted to the system color so that cgColor isn't nil self.colors = [SystemColor(color).cgColor, SystemColor(color).cgColor, SystemColor(color.opacity(0.0)).cgColor] self.locations = [0.0, 0.9, 1.0] } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // Required by the framework public override init(layer: Any) { super.init(layer: layer) } }