// // CanvasFallbackView.swift // YabaiPro // // Created by Jake Shore // Copyright © 2024 Jake Shore. All rights reserved. // import SwiftUI // MARK: - Shared Types // WindowInfo, AnimationSettings are defined in AnimationTypes.swift struct CanvasFallbackView: View { let animationType: AnimationType let windowInfo: AnimationWindowInfo? @Binding var time: Double enum AnimationType: String { case liquidBorder case flowingParticles case rippleEffect case morphingShape case glowingBorder case energyField } var body: some View { Canvas { context, size in switch animationType { case .liquidBorder: renderLiquidBorderCanvas(context: context, size: size, time: Float(time)) case .flowingParticles: renderFlowingParticlesCanvas(context: context, size: size, time: Float(time)) case .rippleEffect: if let point = windowInfo?.focusPoint { renderRippleEffectCanvas(context: context, size: size, time: Float(time), focusPoint: point) } case .morphingShape: renderMorphingShapeCanvas(context: context, size: size, time: Float(time), morphProgress: windowInfo?.morphProgress ?? 0.0) case .glowingBorder: renderGlowingBorderCanvas(context: context, size: size, time: Float(time)) case .energyField: renderEnergyFieldCanvas(context: context, size: size, time: Float(time)) } } .allowsHitTesting(false) } private func renderLiquidBorderCanvas(context: GraphicsContext, size: CGSize, time: Float) { let isActive = windowInfo?.isFocused ?? false let amplitude = isActive ? 4.0 : 1.0 let segments = 32 var path = Path() for i in 0...segments { let t = Double(i) / Double(segments) let x = t * size.width let baseY: CGFloat = 0 // Create flowing wave using sine functions let wave1 = sin(t * 6.28 + Double(time) * 2.0) * amplitude let wave2 = sin(t * 12.56 + Double(time) * 1.5) * amplitude * 0.5 let totalWave = wave1 + wave2 let point = CGPoint(x: x, y: baseY + totalWave) if i == 0 { path.move(to: point) } else { path.addLine(to: point) } } // Create bottom path to close the border path.addLine(to: CGPoint(x: size.width, y: size.height)) path.addLine(to: CGPoint(x: 0, y: size.height)) path.closeSubpath() // Fill with gradient let gradient = Gradient(colors: [ Color.blue.opacity(isActive ? 0.6 : 0.3), Color.cyan.opacity(isActive ? 0.4 : 0.2), Color.blue.opacity(isActive ? 0.2 : 0.1) ]) context.fill(path, with: .linearGradient(gradient, startPoint: .zero, endPoint: CGPoint(x: 0, y: size.height))) context.stroke(path, with: .color(Color.blue.opacity(isActive ? 0.8 : 0.5)), lineWidth: 2) } private func renderFlowingParticlesCanvas(context: GraphicsContext, size: CGSize, time: Float) { let particleCount = 15 for i in 0.. 0 { let radius = rippleTime * 100.0 let alpha = max(0.0, 1.0 - Double(rippleTime) * 0.5) if Double(radius) < Double(maxRadius) { let rect = CGRect(x: focusPoint.x - CGFloat(radius), y: focusPoint.y - CGFloat(radius), width: CGFloat(radius * 2), height: CGFloat(radius * 2)) let path = Path(ellipseIn: rect) // Create ripple with gradient let colors = [Color.blue.opacity(Double(alpha) * 0.6), Color.clear] let gradient = Gradient(colors: colors) context.stroke(path, with: .radialGradient(gradient, center: focusPoint, startRadius: CGFloat(radius * 0.8), endRadius: CGFloat(radius))) } } } } private func renderMorphingShapeCanvas(context: GraphicsContext, size: CGSize, time: Float, morphProgress: Double) { let center = CGPoint(x: size.width/2, y: size.height/2) let baseRadius = min(size.width, size.height) * 0.3 let morphFactor = Float(morphProgress) var path = Path() // Create morphing shape with organic curves let segments = 16 for i in 0..