import SwiftUI import SwiftData import AVKit // MARK: - 自定义过渡动画 extension AnyTransition { /// 创建从左向右的滑动过渡动画 static var slideFromLeading: AnyTransition { .asymmetric( insertion: .move(edge: .trailing).combined(with: .opacity), // 从右侧滑入 removal: .move(edge: .leading).combined(with: .opacity) // 向左侧滑出 ) } } // MARK: - Video Player View struct VideoPlayerView: View { let player: AVPlayer @Binding var isFullscreen: Bool var body: some View { ZStack(alignment: .bottomTrailing) { VideoPlayer(player: player) .frame(width: 300, height: 200) .cornerRadius(12) .overlay( RoundedRectangle(cornerRadius: 12) .stroke(Color.gray.opacity(0.3), lineWidth: 1) ) // 全屏按钮 Button(action: { isFullscreen = true player.play() }) { Image(systemName: "arrow.up.left.and.arrow.down.right") .font(.system(size: 20)) .foregroundColor(.white) .padding(8) .background(Color.black.opacity(0.6)) .clipShape(Circle()) } .padding(16) } .frame(width: 300, height: 200) .onTapGesture { player.play() } .onAppear { player.play() } .fullScreenCover(isPresented: $isFullscreen) { ZStack(alignment: .topLeading) { // 全屏视频播放器 VideoPlayer(player: player) .edgesIgnoringSafeArea(.all) .onAppear { player.play() } // 关闭按钮 Button(action: { isFullscreen = false player.pause() }) { Image(systemName: "xmark.circle.fill") .font(.title) .foregroundColor(.white) .padding() .background(Color.black.opacity(0.4)) .clipShape(Circle()) } .padding(.top, 50) .padding(.leading, 20) } .background(Color.black.edgesIgnoringSafeArea(.all)) .statusBar(hidden: true) } } } // MARK: - 主视图 struct ContentView: View { // MARK: - 状态属性 @State private var showModal = false // 控制用户资料弹窗显示 @State private var showSettings = false // 控制设置页面显示 @State private var contentOffset: CGFloat = 0 // 内容偏移量 @State private var showLogin = false @State private var animateGradient = false @State private var showLottieAnimation = true // 控制Lottie动画显示 @State private var showVideoPlayer = false // 控制视频播放器显示 @State private var isVideoFullscreen = false // 控制视频全屏状态 let timer = Timer.publish(every: 0.02, on: .main, in: .common).autoconnect() // 获取模型上下文 @Environment(\.modelContext) private var modelContext // 查询数据 - 简单查询 @Query private var login: [Login] // 视频播放器 private let player: AVPlayer? init() { // 使用远程视频URL if let videoURL = URL(string: "https://cdn.fairclip.cn/files/7342843896868769793/飞书20250617-144935.mp4") { self.player = AVPlayer(url: videoURL) } else { self.player = nil print("Error: Invalid video URL") } } // MARK: - 主体视图 var body: some View { NavigationView { ZStack { // 全局背景颜色背景色 Color.themeTextWhiteSecondary.ignoresSafeArea() // 主内容区域 VStack { VStack(spacing: 20) { // 顶部导航栏 HStack { // 设置按钮 Button(action: showUserProfile) { SVGImage(svgName: "User") .frame(width: 24, height: 24) } Spacer() // // 测试质感页面入口 // NavigationLink(destination: TestView()) { // Text("TestView") // .font(.subheadline) // .padding(.horizontal, 12) // .padding(.vertical, 6) // .background(Color.brown) // .foregroundColor(.white) // .cornerRadius(8) // } // // 订阅测试按钮 // NavigationLink(destination: SubscribeView()) { // Text("Subscribe") // .font(.subheadline) // .padding(.horizontal, 12) // .padding(.vertical, 6) // .background(Color.orange) // .foregroundColor(.white) // .cornerRadius(8) // } // .padding(.trailing) // .fullScreenCover(isPresented: $showLogin) { // LoginView() // } NavigationLink(destination: SubscribeView()) { Text("3290") .font(Typography.font(for: .subtitle)) .fontWeight(.bold) .padding(.horizontal, 12) .padding(.vertical, 6) .background(Color.black) .foregroundColor(.white) .cornerRadius(16) } .padding(.trailing) .fullScreenCover(isPresented: $showLogin) { LoginView() } } .padding(.horizontal) .padding(.top, 20) // 标题 VStack(alignment: .leading, spacing: 4) { Text("Hi! Click And") Text("Open Your Box~") } .font(Typography.font(for: .smallLargeTitle)) .fontWeight(.bold) .foregroundColor(Color.themeTextMessageMain) .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal) // 盲盒 // 添加SVG背景图片 ZStack { // 1. 背景SVG SVGImage(svgName: "BlindBg") .frame( width: UIScreen.main.bounds.width * 1.8, height: UIScreen.main.bounds.height * 0.65 ) .position(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height * 0.325) // 2. Lottie动画层 if showLottieAnimation { GIFView(name: "Blind") { // 点击事件处理 Router.shared.navigate(to: .blindBox(mediaType: .video)) } .frame(width: 300, height: 300) } // 3. 视频播放器 if showVideoPlayer, let player = player { VideoPlayerView(player: player, isFullscreen: $isVideoFullscreen) } else if showVideoPlayer { Text("Video not found") .foregroundColor(.red) } } .frame( maxWidth: .infinity, maxHeight: UIScreen.main.bounds.height * 0.65 ) .clipped() // 打开 Button(action: showUserProfile) { Text("Go to Buy") .font(Typography.font(for: .body)) .fontWeight(.bold) .frame(maxWidth: .infinity) .padding() .background(Color.themePrimary) .foregroundColor(Color.themeTextMessageMain) .cornerRadius(32) } .padding(.horizontal) } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.themeTextWhiteSecondary) .offset(x: showModal ? UIScreen.main.bounds.width * 0.8 : 0) .animation(.spring(response: 0.6, dampingFraction: 0.8), value: showModal) .edgesIgnoringSafeArea(.all) } // 用户资料弹窗 SlideInModal( isPresented: $showModal, onDismiss: hideUserProfile ) { UserProfileModal( showModal: $showModal, showSettings: $showSettings ) } .offset(x: showSettings ? UIScreen.main.bounds.width : 0) .animation(.spring(response: 0.6, dampingFraction: 0.8), value: showSettings) // 设置页面遮罩层 ZStack { if showSettings { Color.black.opacity(0.3) .edgesIgnoringSafeArea(.all) .onTapGesture(perform: hideSettings) .transition(.opacity) } if showSettings { SettingsView(isPresented: $showSettings) .transition(.move(edge: .leading)) .zIndex(1) } } .animation(.spring(response: 0.6, dampingFraction: 0.8), value: showSettings) } .background(Color.themeTextWhiteSecondary) .navigationBarHidden(true) .navigationBarBackButtonHidden(true) } .navigationViewStyle(StackNavigationViewStyle()) .navigationBarHidden(true) .navigationBarBackButtonHidden(true) } /// 显示用户资料弹窗 private func showUserProfile() { withAnimation(.spring(response: 0.6, dampingFraction: 0.8)) { // print("登录记录数量: \(login.count)") // for (index, item) in login.enumerated() { // print("记录 \(index + 1): 邮箱=\(item.email), 姓名=\(item.name)") // } print("当前登录记录:") for (index, item) in login.enumerated() { print("记录 \(index + 1): 邮箱=\(item.email), 姓名=\(item.name)") } showModal.toggle() } } /// 隐藏用户资料弹窗 private func hideUserProfile() { withAnimation(.spring(response: 0.6, dampingFraction: 0.8)) { showModal = false } } /// 隐藏设置页面 private func hideSettings() { withAnimation(.spring(response: 0.6, dampingFraction: 0.8)) { showSettings = false } } } // MARK: - 预览 #Preview { ContentView() }