diff --git a/wake/View/Blind/BlindBox.swift b/wake/View/Blind/BlindBox.swift index 1c1dbeb..811998f 100644 --- a/wake/View/Blind/BlindBox.swift +++ b/wake/View/Blind/BlindBox.swift @@ -9,8 +9,32 @@ extension Notification.Name { // MARK: - 主视图 struct BlindBoxView: View { @State private var showLottieAnimation = true // 控制Lottie动画显示 + @State private var showScalingOverlay = false + @State private var scale: CGFloat = 0.1 + @State private var mediaToShow: MediaType? + @State private var videoPlayer: AVPlayer? + @State private var showControls = false // Add this line for controlling visibility + + private func startScalingAnimation() { + // Start from 10% size + self.scale = 0.1 + self.showScalingOverlay = true + + // Slower animation with spring effect + withAnimation(.spring(response: 2.0, dampingFraction: 0.5, blendDuration: 0.8)) { + self.scale = 1.0 // Scale enough to cover the screen + } + } + + private func loadVideo() { + // Replace with your actual video URL + if let url = URL(string: "https://cdn.fairclip.cn/files/7342843896868769793/飞书20250617-144935.mp4") { + let player = AVPlayer(url: url) + player.isMuted = true // Mute the video + self.videoPlayer = player + } + } - // MARK: - 主体视图 var body: some View { ZStack { // 全局背景颜色背景色 @@ -29,52 +53,151 @@ struct BlindBoxView: View { .foregroundColor(Color.themeTextMessageMain) .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal) + .opacity(showScalingOverlay ? 0 : 1) + .offset(y: showScalingOverlay ? -UIScreen.main.bounds.height * 0.2 : 0) + .animation(.easeInOut(duration: 0.5), value: showScalingOverlay) + // 盲盒 - // 添加SVG背景图片 ZStack { // 1. 背景SVG - SVGImage(svgName: "BlindBg") - .frame( - width: UIScreen.main.bounds.width * 1.8, - height: UIScreen.main.bounds.height * 0.85 - ) - .position(x: UIScreen.main.bounds.width / 2, - y: UIScreen.main.bounds.height * 0.325) + if !showScalingOverlay { + SVGImage(svgName: "BlindBg") + .frame( + width: UIScreen.main.bounds.width * 1.8, + height: UIScreen.main.bounds.height * 0.85 + ) + .position(x: UIScreen.main.bounds.width / 2, + y: UIScreen.main.bounds.height * 0.325) + .opacity(showScalingOverlay ? 0 : 1) + .animation(.easeOut(duration: 0.5), value: showScalingOverlay) + } - LottieView(name: "data", loopMode: .loop) - .frame(width: 200, height: 200) - .position(x: UIScreen.main.bounds.width / 2, - y: UIScreen.main.bounds.height * 0.325) - .onAppear { - // Load image from URL asynchronously - if let url = URL(string: "https://cdn.fairclip.cn/files/7348219809961742336/c5ca6151-91d3-483e-b7e7-c37f2cb69dc0.png") { - URLSession.shared.dataTask(with: url) { data, response, error in - if let data = data, let image = UIImage(data: data) { - let media = MediaType.image(image) - // Add 5-second delay before navigating - DispatchQueue.main.asyncAfter(deadline: .now() + 5) { - // Navigate to the outcome view using router after delay - Router.shared.navigate(to: .blindOutcome(media: media)) - } - } - }.resume() - } - } + if !showScalingOverlay { + LottieView(name: "data", loopMode: .loop) + .frame(width: 200, height: 200) + .position(x: UIScreen.main.bounds.width / 2, + y: UIScreen.main.bounds.height * 0.325) + .opacity(showScalingOverlay ? 0 : 1) + .animation(.easeOut(duration: 0.5), value: showScalingOverlay) + } } .frame( maxWidth: .infinity, maxHeight: UIScreen.main.bounds.height * 0.65 ) - .clipped() + .opacity(showScalingOverlay ? 0 : 1) + .animation(.easeOut(duration: 0.5), value: showScalingOverlay) + .offset(y: showScalingOverlay ? -100 : 0) + .animation(.easeInOut(duration: 0.5), value: showScalingOverlay) } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.themeTextWhiteSecondary) .edgesIgnoringSafeArea(.all) } + + // Scaling Overlay + if showScalingOverlay { + ZStack { + // Video Player + if let player = videoPlayer { + ZStack(alignment: .topLeading) { + VideoPlayer(player: player) + .aspectRatio(contentMode: .fill) + .frame(width: UIScreen.main.bounds.width * scale, + height: UIScreen.main.bounds.height * scale) + .clipped() + .opacity(scale == 1 ? 1 : 0.7) + .contentShape(Rectangle()) // Make entire area tappable + .onTapGesture { + withAnimation(.easeInOut(duration: 0.3)) { + showControls.toggle() + } + } + .onAppear { + player.seek(to: .zero) + player.play() + NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: player.currentItem, queue: .main) { _ in + player.seek(to: .zero) + player.play() + } + } + + // Back Button - Always on top + if showControls { + Button(action: { + if let media = mediaToShow { + if let url = URL(string: "https://cdn.fairclip.cn/files/7348219809961742336/c5ca6151-91d3-483e-b7e7-c37f2cb69dc0.png") { + URLSession.shared.dataTask(with: url) { data, response, error in + if let data = data, let image = UIImage(data: data) { + let media = MediaType.image(image) + DispatchQueue.main.async { + Router.shared.navigate(to: .blindOutcome(media: media)) + } + } + }.resume() + } + } + }) { + Image(systemName: "chevron.left.circle.fill") + .font(.system(size: 36)) + .foregroundColor(.white) + .padding(12) + .clipShape(Circle()) + } + .padding(.top, 50) + .padding(.leading, 20) + .zIndex(1000) // Ensure it's on top + .transition(.opacity) + } + } + } else { + // Fallback color in case video fails to load + Color.red + .frame(width: UIScreen.main.bounds.width * scale, + height: UIScreen.main.bounds.height * scale) + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .animation(.easeInOut(duration: 3.0), value: scale) + .ignoresSafeArea() + .onTapGesture { + withAnimation(.easeInOut(duration: 0.3)) { + showControls.toggle() + } + } + .onAppear { + // Start animation after a small delay to ensure view is loaded + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + withAnimation(.spring(response: 2.5, dampingFraction: 0.6, blendDuration: 1.0)) { + self.scale = 1.0 + } + } + } + } } .navigationBarBackButtonHidden(true) + .onAppear { + // Load video first + self.loadVideo() + + // Then load image + if let url = URL(string: "https://cdn.fairclip.cn/files/7348219809961742336/c5ca6151-91d3-483e-b7e7-c37f2cb69dc0.png") { + URLSession.shared.dataTask(with: url) { data, response, error in + if let data = data, let image = UIImage(data: data) { + let media = MediaType.image(image) + self.mediaToShow = media + + // Start scaling animation after 5 seconds + DispatchQueue.main.asyncAfter(deadline: .now() + 5) { + self.startScalingAnimation() + } + } + }.resume() + } + } } } + // MARK: - 预览 #Preview { BlindBoxView()