// // ResultBannerView.swift // Baccarat // // Animated result banner showing the winner and winnings. // import SwiftUI /// An animated banner showing the round result. struct ResultBannerView: View { let result: GameResult let winnings: Int @State private var showBanner = false @State private var showText = false @State private var showWinnings = false var body: some View { ZStack { // Background overlay Color.black.opacity(showBanner ? 0.5 : 0) .ignoresSafeArea() .animation(.easeIn(duration: 0.3), value: showBanner) // Banner VStack(spacing: 20) { // Result text Text(result.displayText) .font(.system(size: 36, weight: .black, design: .rounded)) .foregroundStyle( LinearGradient( colors: [.white, result.color], startPoint: .top, endPoint: .bottom ) ) .shadow(color: result.color.opacity(0.8), radius: 10) .scaleEffect(showText ? 1.0 : 0.5) .opacity(showText ? 1.0 : 0) // Winnings display if winnings != 0 { HStack(spacing: 8) { if winnings > 0 { Image(systemName: "plus.circle.fill") .foregroundStyle(.green) Text("\(winnings)") .foregroundStyle(.green) } else { Image(systemName: "minus.circle.fill") .foregroundStyle(.red) Text("\(abs(winnings))") .foregroundStyle(.red) } } .font(.system(size: 28, weight: .bold, design: .rounded)) .scaleEffect(showWinnings ? 1.0 : 0.5) .opacity(showWinnings ? 1.0 : 0) } } .padding(40) .background( RoundedRectangle(cornerRadius: 24) .fill( LinearGradient( colors: [ Color(white: 0.15), Color(white: 0.08) ], startPoint: .top, endPoint: .bottom ) ) .overlay( RoundedRectangle(cornerRadius: 24) .strokeBorder( LinearGradient( colors: [ result.color.opacity(0.8), result.color.opacity(0.3) ], startPoint: .topLeading, endPoint: .bottomTrailing ), lineWidth: 3 ) ) ) .shadow(color: result.color.opacity(0.3), radius: 30) .scaleEffect(showBanner ? 1.0 : 0.8) .opacity(showBanner ? 1.0 : 0) } .onAppear { withAnimation(.spring(duration: 0.4, bounce: 0.3)) { showBanner = true } withAnimation(.spring(duration: 0.4, bounce: 0.3).delay(0.2)) { showText = true } withAnimation(.spring(duration: 0.4, bounce: 0.3).delay(0.4)) { showWinnings = true } } } } /// Confetti particle for celebrations. struct ConfettiPiece: View { let color: Color @State private var position: CGPoint = .zero @State private var rotation: Double = 0 @State private var opacity: Double = 1 var body: some View { Rectangle() .fill(color) .frame(width: 8, height: 12) .rotationEffect(.degrees(rotation)) .position(position) .opacity(opacity) .onAppear { let screenWidth = 400.0 let startX = Double.random(in: 0...screenWidth) position = CGPoint(x: startX, y: -20) withAnimation(.easeIn(duration: Double.random(in: 2...4))) { position = CGPoint( x: startX + Double.random(in: -100...100), y: 800 ) rotation = Double.random(in: 360...1080) opacity = 0 } } } } /// A confetti celebration overlay. struct ConfettiView: View { let colors: [Color] = [.red, .blue, .green, .yellow, .orange, .purple, .pink] var body: some View { ZStack { ForEach(0..<50, id: \.self) { _ in ConfettiPiece(color: colors.randomElement() ?? .yellow) } } .allowsHitTesting(false) } } #Preview { ZStack { Color(red: 0.0, green: 0.3, blue: 0.15) .ignoresSafeArea() ResultBannerView(result: .playerWins, winnings: 500) } }