From 40ef986c6f55be8b40dc8922818dfa36c191cf8a Mon Sep 17 00:00:00 2001 From: Junhui Chen Date: Sat, 6 Sep 2025 18:36:13 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=88=A0=E9=99=A4=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E6=97=A0=E7=94=A8=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wake/View/Blind/AvatarBox.swift | 105 ------ wake/View/Blind/BlindBox.swift | 0 wake/View/Blind/Box.swift | 301 ------------------ wake/View/Blind/Box1.swift | 253 --------------- wake/View/Blind/Box3.swift | 226 ------------- wake/View/Blind/Box4.swift | 140 -------- wake/View/Blind/Box5.swift | 222 ------------- wake/View/Blind/Box6.swift | 250 --------------- .../View/{Blind => Subscribe}/JoinModal.swift | 0 9 files changed, 1497 deletions(-) delete mode 100644 wake/View/Blind/AvatarBox.swift delete mode 100644 wake/View/Blind/BlindBox.swift delete mode 100644 wake/View/Blind/Box.swift delete mode 100644 wake/View/Blind/Box1.swift delete mode 100644 wake/View/Blind/Box3.swift delete mode 100644 wake/View/Blind/Box4.swift delete mode 100644 wake/View/Blind/Box5.swift delete mode 100644 wake/View/Blind/Box6.swift rename wake/View/{Blind => Subscribe}/JoinModal.swift (100%) diff --git a/wake/View/Blind/AvatarBox.swift b/wake/View/Blind/AvatarBox.swift deleted file mode 100644 index de7f62a..0000000 --- a/wake/View/Blind/AvatarBox.swift +++ /dev/null @@ -1,105 +0,0 @@ -import SwiftUI - -struct AvatarBoxView: View { - @Environment(\.dismiss) private var dismiss - @EnvironmentObject private var router: Router - @State private var isAnimating = false - - var body: some View { - ZStack { - // Background color - Color.white - .ignoresSafeArea() - - VStack(spacing: 0) { - // Navigation Bar - HStack { - Button(action: { - dismiss() - }) { - Image(systemName: "chevron.left") - .font(.system(size: 17, weight: .medium)) - .foregroundColor(.black) - .padding() - } - - Spacer() - - Text("动画页面") - .font(.headline) - .foregroundColor(.black) - - Spacer() - - // Invisible spacer to center the title - Color.clear - .frame(width: 44, height: 44) - } - .frame(height: 44) - .background(Color.white) - - Spacer() - - // Animated Content - ZStack { - // Pulsing circle animation - Circle() - .fill(Color.blue.opacity(0.2)) - .frame(width: 200, height: 200) - .scaleEffect(isAnimating ? 1.5 : 1.0) - .opacity(isAnimating ? 0.5 : 1.0) - .animation( - Animation.easeInOut(duration: 1.5) - .repeatForever(autoreverses: true), - value: isAnimating - ) - - // Center icon - Image(systemName: "sparkles") - .font(.system(size: 60)) - .foregroundColor(.blue) - .rotationEffect(.degrees(isAnimating ? 360 : 0)) - .animation( - Animation.linear(duration: 8) - .repeatForever(autoreverses: false), - value: isAnimating - ) - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - - Spacer() - - // Bottom Button - Button(action: { - router.navigate(to: .feedbackView) - }) { - Text("Continue") - .font(.headline) - .foregroundColor(.themeTextMessageMain) - .frame(maxWidth: .infinity) - .frame(height: 56) - .background(Color.themePrimary) - .cornerRadius(25) - .padding(.horizontal, 24) - } - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - } - .navigationBarBackButtonHidden(true) - .navigationBarHidden(true) - .onAppear { - isAnimating = true - } - } -} - -// MARK: - Preview -struct AvatarBoxView_Previews: PreviewProvider { - static var previews: some View { - NavigationView { - AvatarBoxView() - .environmentObject(Router.shared) - } - .navigationViewStyle(StackNavigationViewStyle()) - } -} diff --git a/wake/View/Blind/BlindBox.swift b/wake/View/Blind/BlindBox.swift deleted file mode 100644 index e69de29..0000000 diff --git a/wake/View/Blind/Box.swift b/wake/View/Blind/Box.swift deleted file mode 100644 index 9da0c25..0000000 --- a/wake/View/Blind/Box.swift +++ /dev/null @@ -1,301 +0,0 @@ -import SwiftUI - -struct FilmStripView: View { - @State private var animate = false - // 使用SF Symbols名称数组 - private let symbolNames = [ - "photo.fill", "heart.fill", "star.fill", "bookmark.fill", - "flag.fill", "bell.fill", "tag.fill", "paperplane.fill" - ] - private let targetIndices = [2, 5, 3] // 每条胶片最终停止的位置 - - var body: some View { - ZStack { - Color.black.edgesIgnoringSafeArea(.all) - - // 三条胶片带 - FilmStrip( - symbols: symbolNames, - targetIndex: targetIndices[0], - offset: 0, - stripColor: .red - ) - .rotationEffect(.degrees(5)) - .zIndex(1) - - FilmStrip( - symbols: symbolNames, - targetIndex: targetIndices[1], - offset: 0.3, - stripColor: .blue - ) - .rotationEffect(.degrees(-3)) - .zIndex(2) - - FilmStrip( - symbols: symbolNames, - targetIndex: targetIndices[2], - offset: 0.6, - stripColor: .green - ) - .rotationEffect(.degrees(2)) - .zIndex(3) - } - .onAppear { - withAnimation( - .timingCurve(0.2, 0.1, 0.8, 0.9, duration: 4.0) - ) { - animate = true - } - } - } -} - -// 单个胶片带视图 -struct FilmStrip: View { - let symbols: [String] - let targetIndex: Int - let offset: Double - let stripColor: Color - @State private var animate = false - - var body: some View { - GeometryReader { geometry in - let itemWidth: CGFloat = 100 - let spacing: CGFloat = 8 - let totalWidth = itemWidth * CGFloat(symbols.count) + spacing * CGFloat(symbols.count - 1) - - // 胶片背景 - RoundedRectangle(cornerRadius: 10) - .fill(stripColor.opacity(0.8)) - .frame(height: 160) - .overlay( - // 胶片齿孔 - HStack(spacing: spacing) { - ForEach(0.. CGFloat { - let baseDistance: CGFloat = 1000 - let speedFactor: CGFloat = 1.0 - - return baseDistance * speedFactor * progressCurve() - } - - // 中间正胶卷偏移量计算(向左移动) - private func calculateMiddleOffset() -> CGFloat { - let baseDistance: CGFloat = -1100 - let speedFactor: CGFloat = 1.05 - - return baseDistance * speedFactor * progressCurve() - } - - // 下方倾斜胶卷偏移量计算(向右移动) - private func calculateBottomOffset() -> CGFloat { - let baseDistance: CGFloat = 1000 - let speedFactor: CGFloat = 0.95 - - return baseDistance * speedFactor * progressCurve() - } - - // 动画曲线:先慢后快,最后卡顿 - private func progressCurve() -> CGFloat { - if animationProgress < 0.6 { - // 初期加速阶段 - return easeInQuad(animationProgress / 0.6) * 0.7 - } else if animationProgress < 0.85 { - // 高速移动阶段 - return 0.7 + easeOutQuad((animationProgress - 0.6) / 0.25) * 0.25 - } else { - // 卡顿阶段 - let t = (animationProgress - 0.85) / 0.15 - return 0.95 + t * 0.05 - } - } - - // 缓入曲线 - private func easeInQuad(_ t: CGFloat) -> CGFloat { - return t * t - } - - // 缓出曲线 - private func easeOutQuad(_ t: CGFloat) -> CGFloat { - return t * (2 - t) - } - - // 启动动画序列 - private func startAnimation() { - // 第一阶段:逐渐加速 - withAnimation(.easeIn(duration: 3.5)) { - animationProgress = 0.6 - } - - // 第二阶段:高速移动 - DispatchQueue.main.asyncAfter(deadline: .now() + 3.5) { - withAnimation(.linear(duration: 2.5)) { - animationProgress = 0.85 - } - - // 第三阶段:卡顿效果 - DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) { - withAnimation(.easeOut(duration: 1.8)) { - animationProgress = 1.0 - isCatching = true - } - - // 卡顿后重合消失,显示目标图片 - DispatchQueue.main.asyncAfter(deadline: .now() + 1.8) { - withAnimation(.easeInOut(duration: 0.7)) { - isDisappearing = true - } - - // 显示重复播放按钮 - DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { - withAnimation(.easeInOut(duration: 0.3)) { - showReplayButton = true - } - } - } - } - } - } -} - -// 电影胶卷视图组件 -struct FilmReelView1: View { - let images: [String] - - var body: some View { - HStack(spacing: 10) { - ForEach(images.indices, id: \.self) { index in - ZStack { - // 胶卷边框 - RoundedRectangle(cornerRadius: 4) - .stroke(Color.gray, lineWidth: 2) - .background(Color(red: 0.15, green: 0.15, blue: 0.15)) - - // 图片内容 - Rectangle() - .fill( - LinearGradient( - gradient: Gradient(colors: [.blue, .indigo]), - startPoint: .topLeading, - endPoint: .bottomTrailing - ) - ) - .opacity(0.9) - .cornerRadius(2) - .padding(2) - - // 模拟图片文本 - Text("\(images[index])") - .foregroundColor(.white) - .font(.caption2) - } - .frame(width: 90, height: 130) - // 胶卷孔洞 - .overlay( - HStack { - VStack(spacing: 6) { - ForEach(0..<6) { _ in - Circle() - .frame(width: 6, height: 6) - .foregroundColor(.gray) - } - } - Spacer() - VStack(spacing: 6) { - ForEach(0..<6) { _ in - Circle() - .frame(width: 6, height: 6) - .foregroundColor(.gray) - } - } - } - ) - } - } - } -} - -// 预览 -struct ReplayableFilmReelAnimation_Previews: PreviewProvider { - static var previews: some View { - ReplayableFilmReelAnimation() - } -} - \ No newline at end of file diff --git a/wake/View/Blind/Box3.swift b/wake/View/Blind/Box3.swift deleted file mode 100644 index c482278..0000000 --- a/wake/View/Blind/Box3.swift +++ /dev/null @@ -1,226 +0,0 @@ -import SwiftUI - -struct FilmAnimation1: View { - // 设备尺寸 - private let deviceWidth = UIScreen.main.bounds.width - private let deviceHeight = UIScreen.main.bounds.height - - // 动画状态控制 - @State private var animationProgress: CGFloat = 0.0 // 0-1总进度 - @State private var isAnimating: Bool = false - @State private var animationComplete: Bool = false - - // 胶卷数据 - private let reelImages: [[String]] = [ - (0..<150).map { "film1-\($0+1)" }, // 上方胶卷 - (0..<180).map { "film2-\($0+1)" }, // 中间胶卷(垂直) - (0..<150).map { "film3-\($0+1)" } // 下方胶卷 - ] - - // 胶卷参数 - private let frameWidth: CGFloat = 90 - private let frameHeight: CGFloat = 130 - private let frameSpacing: CGFloat = 10 - private let totalDistance: CGFloat = 2000 // 总移动距离 - - // 动画时间参数 - private let accelerationDuration: Double = 5.0 // 加速阶段时长(0-5s) - private let constantSpeedDuration: Double = 6.0 // 匀速+放大阶段时长(5-11s) - private var totalDuration: Double { accelerationDuration + constantSpeedDuration } - private var scaleStartProgress: CGFloat { accelerationDuration / totalDuration } - private let finalScale: CGFloat = 3.0 // 展示完整胶片的缩放比例 - - // 对称布局核心参数(重点调整) - private let symmetricTiltAngle: Double = 8 // 减小倾斜角度,增强对称感 - private let verticalOffset: CGFloat = 140 // 减小垂直距离,靠近中间胶卷 - private let initialMiddleY: CGFloat = 50 // 中间胶卷初始位置上移,缩短与上下距离 - - // 上下胶卷与中间胶卷的初始水平偏移(确保视觉对称) - private let horizontalOffset: CGFloat = 30 - - var body: some View { - ZStack { - // 深色背景 - Color(red: 0.08, green: 0.08, blue: 0.08) - .edgesIgnoringSafeArea(.all) - - // 上方倾斜胶卷(左高右低,与中间距离适中) - FilmReelView3(images: reelImages[0]) - .rotationEffect(Angle(degrees: -symmetricTiltAngle)) - .offset(x: topReelPosition - horizontalOffset, y: -verticalOffset) // 水平微调增强对称 - .opacity(upperLowerOpacity) - .zIndex(1) - - // 下方倾斜胶卷(左低右高,与中间距离适中) - FilmReelView3(images: reelImages[2]) - .rotationEffect(Angle(degrees: symmetricTiltAngle)) - .offset(x: bottomReelPosition + horizontalOffset, y: verticalOffset) // 水平微调增强对称 - .opacity(upperLowerOpacity) - .zIndex(1) - - // 中间胶卷(垂直居中) - FilmReelView3(images: reelImages[1]) - .offset(x: middleReelPosition, y: middleYPosition) - .scaleEffect(currentScale) - .position(centerPosition) - .zIndex(2) - .edgesIgnoringSafeArea(.all) - } - .onAppear { - startAnimation() - } - } - - // MARK: - 动画逻辑 - - private func startAnimation() { - guard !isAnimating && !animationComplete else { return } - isAnimating = true - - withAnimation(Animation.timingCurve(0.2, 0.0, 0.8, 1.0, duration: totalDuration)) { - animationProgress = 1.0 - } - - DispatchQueue.main.asyncAfter(deadline: .now() + totalDuration) { - isAnimating = false - animationComplete = true - } - } - - // MARK: - 动画计算 - - private var currentScale: CGFloat { - guard animationProgress >= scaleStartProgress else { - return 1.0 - } - - let scalePhaseProgress = (animationProgress - scaleStartProgress) / (1.0 - scaleStartProgress) - return 1.0 + (finalScale - 1.0) * scalePhaseProgress - } - - // 中间胶卷Y轴位置(微调至更居中) - private var middleYPosition: CGFloat { - if animationProgress < scaleStartProgress { - return initialMiddleY - (initialMiddleY * (animationProgress / scaleStartProgress)) - } else { - return 0 // 5s后精准居中 - } - } - - private var upperLowerOpacity: Double { - if animationProgress < scaleStartProgress { - return 0.8 - } else { - let fadeProgress = (animationProgress - scaleStartProgress) / (1.0 - scaleStartProgress) - return 0.8 * (1.0 - fadeProgress) - } - } - - private var centerPosition: CGPoint { - CGPoint(x: deviceWidth / 2, y: deviceHeight / 2) - } - - // MARK: - 位置计算(确保对称运动) - - private var motionProgress: CGFloat { - if animationProgress < scaleStartProgress { - let t = animationProgress / scaleStartProgress - return t * t // 加速阶段 - } else { - return 1.0 + (animationProgress - scaleStartProgress) * - (scaleStartProgress / (1.0 - scaleStartProgress)) - } - } - - // 上方胶卷位置(与下方保持对称速度) - private var topReelPosition: CGFloat { - totalDistance * 0.9 * motionProgress - } - - // 中间胶卷位置(主视觉移动) - private var middleReelPosition: CGFloat { - -totalDistance * 1.2 * motionProgress - } - - // 下方胶卷位置(与上方保持对称速度) - private var bottomReelPosition: CGFloat { - totalDistance * 0.9 * motionProgress // 与上方速度完全一致 - } -} - -// MARK: - 胶卷组件 - -struct FilmReelView3: View { - let images: [String] - - var body: some View { - HStack(spacing: 10) { - ForEach(images.indices, id: \.self) { index in - FilmFrameView3(imageName: images[index]) - } - } - } -} - -struct FilmFrameView3: View { - let imageName: String - - var body: some View { - ZStack { - // 胶卷边框 - RoundedRectangle(cornerRadius: 4) - .stroke(Color.gray, lineWidth: 2) - .background(Color(red: 0.15, green: 0.15, blue: 0.15)) - - // 帧内容 - Rectangle() - .fill(gradientColor) - .cornerRadius(2) - .padding(2) - - // 帧标识 - Text(imageName) - .foregroundColor(.white) - .font(.caption2) - } - .frame(width: 90, height: 130) - // 胶卷孔洞 - .overlay( - HStack { - VStack(spacing: 6) { - ForEach(0..<6) { _ in - Circle() - .frame(width: 6, height: 6) - .foregroundColor(.gray) - } - } - Spacer() - VStack(spacing: 6) { - ForEach(0..<6) { _ in - Circle() - .frame(width: 6, height: 6) - .foregroundColor(.gray) - } - } - } - ) - } - - private var gradientColor: LinearGradient { - if imageName.hasPrefix("film1") { - return LinearGradient(gradient: Gradient(colors: [.blue, .indigo]), startPoint: .topLeading, endPoint: .bottomTrailing) - } else if imageName.hasPrefix("film2") { - return LinearGradient(gradient: Gradient(colors: [.yellow, .orange]), startPoint: .topLeading, endPoint: .bottomTrailing) - } else { - return LinearGradient(gradient: Gradient(colors: [.teal, .cyan]), startPoint: .topLeading, endPoint: .bottomTrailing) - } - } -} - -// 预览 -struct FilmAnimation_Previews3: PreviewProvider { - static var previews: some View { - FilmAnimation1() - } -} - \ No newline at end of file diff --git a/wake/View/Blind/Box4.swift b/wake/View/Blind/Box4.swift deleted file mode 100644 index 0ef9004..0000000 --- a/wake/View/Blind/Box4.swift +++ /dev/null @@ -1,140 +0,0 @@ -import SwiftUI - -// MARK: - 主视图:电影胶卷盲盒动效 -struct FilmStripBlindBoxView: View { - @State private var isAnimating = false - @State private var revealCenter = false - - // 三格盲盒内容(使用 SF Symbols 模拟不同“隐藏款”) - let boxContents = ["popcorn", "star", "music.note"] - - var body: some View { - GeometryReader { geometry in - let width = geometry.size.width - - ZStack { - // 左边盲盒胶卷帧 - BlindBoxFrame(symbol: boxContents[0]) - .offset(x: isAnimating ? -width / 4 : -width) - .opacity(isAnimating ? 1 : 0) - - // 中间盲盒胶卷帧(最终放大) - BlindBoxFrame(symbol: boxContents[1]) - .scaleEffect(revealCenter ? 1.6 : 1) - .offset(x: isAnimating ? 0 : width) - .opacity(isAnimating ? 1 : 0) - - // 右边盲盒胶卷帧 - BlindBoxFrame(symbol: boxContents[2]) - .offset(x: isAnimating ? width / 4 : width * 1.5) - .opacity(isAnimating ? 1 : 0) - } - .onAppear { - // 第一阶段:胶卷滑入 - withAnimation(.easeOut(duration: 1.0)) { - isAnimating = true - } - - // 第二阶段:中间帧“开盒”放大 - DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) { - withAnimation( - .interpolatingSpring(stiffness: 80, damping: 12).delay(0.3) - ) { - revealCenter = true - } - } - } - } - .frame(height: 140) - .padding() - .background(Color.black.opacity(0.05)) - } -} - -// MARK: - 盲盒胶卷帧:带孔 + 橙色背景 + SF Symbol -struct BlindBoxFrame: View { - let symbol: String - - var body: some View { - ZStack { - // 胶片边框(橙色 + 打孔) - FilmBorder() - - // SF Symbol 作为“盲盒内容” - Image(systemName: symbol) - .resizable() - .scaledToFit() - .foregroundColor(.white.opacity(0.85)) - .frame(width: 60, height: 60) - } - .frame(width: 120, height: 120) - } -} - -// MARK: - 胶片边框:#FFB645 背景 + 打孔 -struct FilmBorder: View { - var body: some View { - Canvas { context, size in - let w = size.width - let h = size.height - - // 背景色:FFB645 - let bgColor = Color(hex: 0xFFB645) - context.fill(Path(CGRect(origin: .zero, size: size)), with: .color(bgColor)) - - // 打孔参数 - let holeRadius: CGFloat = 3.5 - let margin: CGFloat = 12 - let holeYOffset: CGFloat = h * 0.25 - - // 左侧打孔(3个) - for i in 0..<3 { - let y = CGFloat(i + 1) * (h / 4) - context.fill( - Path(ellipseIn: CGRect( - x: margin - holeRadius * 2, - y: y - holeRadius, - width: holeRadius * 2, - height: holeRadius * 2 - )), - with: .color(.black) - ) - } - - // 右侧打孔(3个) - for i in 0..<3 { - let y = CGFloat(i + 1) * (h / 4) - context.fill( - Path(ellipseIn: CGRect( - x: w - margin, - y: y - holeRadius, - width: holeRadius * 2, - height: holeRadius * 2 - )), - with: .color(.black) - ) - } - } - } -} - -// MARK: - Color 扩展:支持 HEX 颜色 -extension Color { - init(hex: UInt) { - self.init( - .sRGB, - red: Double((hex >> 16) & 0xff) / 255, - green: Double((hex >> 8) & 0xff) / 255, - blue: Double(hex & 0xff) / 255, - opacity: 1.0 - ) - } -} - -// MARK: - 预览 -struct FilmStripBlindBoxView_Previews: PreviewProvider { - static var previews: some View { - FilmStripBlindBoxView() - .preferredColorScheme(.dark) - } -} \ No newline at end of file diff --git a/wake/View/Blind/Box5.swift b/wake/View/Blind/Box5.swift deleted file mode 100644 index 93dd160..0000000 --- a/wake/View/Blind/Box5.swift +++ /dev/null @@ -1,222 +0,0 @@ -import SwiftUI - -struct FilmAnimation5: View { - // 设备尺寸 - private let deviceWidth = UIScreen.main.bounds.width - private let deviceHeight = UIScreen.main.bounds.height - - // 动画状态控制 - @State private var animationProgress: CGFloat = 0.0 // 0-1总进度 - @State private var isAnimating: Bool = false - @State private var animationComplete: Bool = false - - // 胶卷数据 - private let reelImages: [[String]] = [ - (0..<150).map { "film1-\($0+1)" }, // 上方倾斜胶卷 - (0..<180).map { "film2-\($0+1)" }, // 中间胶卷 - (0..<150).map { "film3-\($0+1)" } // 下方倾斜胶卷 - ] - - // 胶卷参数 - private let frameWidth: CGFloat = 90 - private let frameHeight: CGFloat = 130 - private let totalDistance: CGFloat = 1800 // 总移动距离 - - // 动画阶段时间参数(核心调整) - private let accelerationDuration: Double = 5.0 // 0-5s加速 - private let constantSpeedDuration: Double = 1.0 // 5-6s匀速移动 - private let scaleDuration: Double = 2.0 // 6-8s共同放大 - private var totalDuration: Double { accelerationDuration + constantSpeedDuration + scaleDuration } - - // 各阶段进度阈值 - private var accelerationEnd: CGFloat { accelerationDuration / totalDuration } - private var constantSpeedEnd: CGFloat { (accelerationDuration + constantSpeedDuration) / totalDuration } - - // 对称倾斜参数 - private let symmetricTiltAngle: Double = 10 // 上下胶卷对称倾斜角度 - private let verticalOffset: CGFloat = 120 // 上下胶卷垂直距离(对称) - private let finalScale: CGFloat = 4.0 // 最终放大倍数 - - var body: some View { - ZStack { - // 深色背景 - Color(red: 0.08, green: 0.08, blue: 0.08) - .edgesIgnoringSafeArea(.all) - - // 上方倾斜胶卷(向右移动) - FilmReelView5(images: reelImages[0]) - .rotationEffect(Angle(degrees: -symmetricTiltAngle)) - .offset(x: topReelPosition, y: -verticalOffset) - .scaleEffect(currentScale) - .opacity(upperLowerOpacity) - .zIndex(2) - - // 下方倾斜胶卷(向右移动) - FilmReelView5(images: reelImages[2]) - .rotationEffect(Angle(degrees: symmetricTiltAngle)) - .offset(x: bottomReelPosition, y: verticalOffset) - .scaleEffect(currentScale) - .opacity(upperLowerOpacity) - .zIndex(2) - - // 中间胶卷(向左移动,最终保留) - FilmReelView5(images: reelImages[1]) - .offset(x: middleReelPosition, y: 0) - .scaleEffect(currentScale) - .opacity(1.0) // 始终不透明 - .zIndex(1) - .edgesIgnoringSafeArea(.all) - } - .onAppear { - startAnimation() - } - } - - // MARK: - 动画逻辑 - - private func startAnimation() { - guard !isAnimating && !animationComplete else { return } - isAnimating = true - - // 分阶段动画曲线:先加速后匀速 - withAnimation(Animation.timingCurve(0.3, 0.0, 0.7, 1.0, duration: totalDuration)) { - animationProgress = 1.0 - } - - // 动画结束标记 - DispatchQueue.main.asyncAfter(deadline: .now() + totalDuration) { - isAnimating = false - animationComplete = true - } - } - - // MARK: - 动画计算 - - // 共同放大比例(6s后开始放大) - private var currentScale: CGFloat { - guard animationProgress >= constantSpeedEnd else { - return 1.0 // 前6s保持原尺寸 - } - - // 放大阶段相对进度(0-1) - let scalePhaseProgress = (animationProgress - constantSpeedEnd) / (1.0 - constantSpeedEnd) - return 1.0 + (finalScale - 1.0) * scalePhaseProgress - } - - // 上下胶卷透明度(放大阶段逐渐隐藏) - private var upperLowerOpacity: Double { - guard animationProgress >= constantSpeedEnd else { - return 0.8 // 前6s保持可见 - } - - // 放大阶段同步淡出 - let fadeProgress = (animationProgress - constantSpeedEnd) / (1.0 - constantSpeedEnd) - return 0.8 * (1.0 - fadeProgress) - } - - // MARK: - 移动速度控制(确保匀速阶段速度一致) - - private var motionProgress: CGFloat { - if animationProgress < accelerationEnd { - // 0-5s加速阶段:二次方曲线加速 - let t = animationProgress / accelerationEnd - return t * t - } else { - // 5s后匀速阶段:保持最大速度 - return 1.0 + (animationProgress - accelerationEnd) * - (accelerationEnd / (1.0 - accelerationEnd)) - } - } - - // 上方胶卷位置(向右移动) - private var topReelPosition: CGFloat { - totalDistance * 0.8 * motionProgress - } - - // 中间胶卷位置(向左移动) - private var middleReelPosition: CGFloat { - -totalDistance * 0.8 * motionProgress // 与上下胶卷速度大小相同,方向相反 - } - - // 下方胶卷位置(向右移动) - private var bottomReelPosition: CGFloat { - totalDistance * 0.8 * motionProgress // 与上方胶卷速度完全一致,保持对称 - } -} - -// MARK: - 胶卷组件 - -struct FilmReelView5: View { - let images: [String] - - var body: some View { - HStack(spacing: 10) { - ForEach(images.indices, id: \.self) { index in - FilmFrameView5(imageName: images[index]) - } - } - } -} - -struct FilmFrameView5: View { - let imageName: String - - var body: some View { - ZStack { - // 胶卷边框 - RoundedRectangle(cornerRadius: 4) - .stroke(Color.gray, lineWidth: 2) - .background(Color(red: 0.15, green: 0.15, blue: 0.15)) - - // 帧内容 - Rectangle() - .fill(gradientColor) - .cornerRadius(2) - .padding(2) - - // 帧标识 - Text(imageName) - .foregroundColor(.white) - .font(.caption2) - } - .frame(width: 90, height: 130) - // 胶卷孔洞 - .overlay( - HStack { - VStack(spacing: 6) { - ForEach(0..<6) { _ in - Circle() - .frame(width: 6, height: 6) - .foregroundColor(.gray) - } - } - Spacer() - VStack(spacing: 6) { - ForEach(0..<6) { _ in - Circle() - .frame(width: 6, height: 6) - .foregroundColor(.gray) - } - } - } - ) - } - - private var gradientColor: LinearGradient { - if imageName.hasPrefix("film1") { - return LinearGradient(gradient: Gradient(colors: [.blue, .indigo]), startPoint: .topLeading, endPoint: .bottomTrailing) - } else if imageName.hasPrefix("film2") { - return LinearGradient(gradient: Gradient(colors: [.yellow, .orange]), startPoint: .topLeading, endPoint: .bottomTrailing) - } else { - return LinearGradient(gradient: Gradient(colors: [.teal, .cyan]), startPoint: .topLeading, endPoint: .bottomTrailing) - } - } -} - -// 预览 -struct FilmAnimation_Previews5: PreviewProvider { - static var previews: some View { - FilmAnimation5() - } -} - \ No newline at end of file diff --git a/wake/View/Blind/Box6.swift b/wake/View/Blind/Box6.swift deleted file mode 100644 index 5ba1d07..0000000 --- a/wake/View/Blind/Box6.swift +++ /dev/null @@ -1,250 +0,0 @@ -import SwiftUI - -struct FilmAnimation: View { - // 设备尺寸 - private let deviceWidth = UIScreen.main.bounds.width - private let deviceHeight = UIScreen.main.bounds.height - - // 动画状态控制 - @State private var animationProgress: CGFloat = 0.0 // 0-1总进度 - @State private var isAnimating: Bool = false - @State private var animationComplete: Bool = false - - // 胶卷数据 - private let reelImages: [[String]] = [ - (0..<300).map { "film1-\($0+1)" }, // 上方胶卷 - (0..<350).map { "film2-\($0+1)" }, // 中间胶卷 - (0..<300).map { "film3-\($0+1)" } // 下方胶卷 - ] - - // 胶卷参数 - private let frameWidth: CGFloat = 90 - private let frameHeight: CGFloat = 130 - private let frameSpacing: CGFloat = 12 - - // 动画阶段时间参数 - private let accelerationDuration: Double = 5.0 // 0-5s加速 - private let constantSpeedDuration: Double = 1.0 // 5-6s匀速 - private let scaleStartDuration: Double = 1.0 // 6-7s共同放大 - private let scaleFinishDuration: Double = 1.0 // 7-8s仅中间胶卷放大 - private var totalDuration: Double { - accelerationDuration + constantSpeedDuration + scaleStartDuration + scaleFinishDuration - } - - // 各阶段进度阈值 - private var accelerationEnd: CGFloat { accelerationDuration / totalDuration } - private var constantSpeedEnd: CGFloat { (accelerationDuration + constantSpeedDuration) / totalDuration } - private var scaleStartEnd: CGFloat { - (accelerationDuration + constantSpeedDuration + scaleStartDuration) / totalDuration - } - - // 布局与运动参数(核心:对称倾斜角度) - private let tiltAngle: Double = 10 // 基础倾斜角度 - private let upperTilt: Double = -10 // 上方胶卷:左高右低(负角度) - private let lowerTilt: Double = 10 // 下方胶卷:左低右高(正角度) - private let verticalSpacing: CGFloat = 200 // 上下胶卷垂直间距 - private let finalScale: CGFloat = 4.5 - - // 移动距离参数 - private let maxTiltedReelMovement: CGFloat = 3500 // 倾斜胶卷最大移动距离 - private let maxMiddleReelMovement: CGFloat = -3000 // 中间胶卷最大移动距离 - - var body: some View { - // 固定背景 - Color(red: 0.08, green: 0.08, blue: 0.08) - .edgesIgnoringSafeArea(.all) - .overlay( - ZStack { - // 上方倾斜胶卷(左高右低,向右移动) - if showTiltedReels { - FilmReelView(images: reelImages[0]) - .rotationEffect(Angle(degrees: upperTilt)) // 左高右低 - .offset(x: upperReelXPosition, y: -verticalSpacing/2) - .scaleEffect(tiltedScale) - .opacity(tiltedOpacity) - .zIndex(1) - } - - // 下方倾斜胶卷(左低右高,向右移动) - if showTiltedReels { - FilmReelView(images: reelImages[2]) - .rotationEffect(Angle(degrees: lowerTilt)) // 左低右高 - .offset(x: lowerReelXPosition, y: verticalSpacing/2) - .scaleEffect(tiltedScale) - .opacity(tiltedOpacity) - .zIndex(1) - } - - // 中间胶卷(垂直,向左移动) - FilmReelView(images: reelImages[1]) - .offset(x: middleReelXPosition, y: 0) - .scaleEffect(middleScale) - .opacity(1.0) - .zIndex(2) - .edgesIgnoringSafeArea(.all) - } - ) - .onAppear { - startAnimation() - } - } - - // MARK: - 动画逻辑 - - private func startAnimation() { - guard !isAnimating && !animationComplete else { return } - isAnimating = true - - withAnimation(Animation.easeInOut(duration: totalDuration)) { - animationProgress = 1.0 - } - - DispatchQueue.main.asyncAfter(deadline: .now() + totalDuration) { - isAnimating = false - animationComplete = true - } - } - - // MARK: - 位置计算(确保向右移动) - - // 上方倾斜胶卷X位置 - private var upperReelXPosition: CGFloat { - let startPosition: CGFloat = -deviceWidth * 1.2 // 左侧屏幕外起始 - return startPosition + (maxTiltedReelMovement * movementProgress) - } - - // 下方倾斜胶卷X位置 - private var lowerReelXPosition: CGFloat { - let startPosition: CGFloat = -deviceWidth * 0.8 // 稍右于上方胶卷起始 - return startPosition + (maxTiltedReelMovement * movementProgress) - } - - // 中间胶卷X位置 - private var middleReelXPosition: CGFloat { - let startPosition: CGFloat = deviceWidth * 0.3 - return startPosition + (maxMiddleReelMovement * movementProgress) - } - - // 移动进度(0-1) - private var movementProgress: CGFloat { - if animationProgress < constantSpeedEnd { - return animationProgress / constantSpeedEnd - } else { - return 1.0 // 6秒后停止移动 - } - } - - // MARK: - 缩放与显示控制 - - // 中间胶卷缩放 - private var middleScale: CGFloat { - guard animationProgress >= constantSpeedEnd else { - return 1.0 - } - - let scalePhaseProgress = (animationProgress - constantSpeedEnd) / (1.0 - constantSpeedEnd) - return 1.0 + (finalScale - 1.0) * scalePhaseProgress - } - - // 倾斜胶卷缩放 - private var tiltedScale: CGFloat { - guard animationProgress >= constantSpeedEnd, animationProgress < scaleStartEnd else { - return 1.0 - } - - let scalePhaseProgress = (animationProgress - constantSpeedEnd) / (scaleStartEnd - constantSpeedEnd) - return 1.0 + (finalScale * 0.6 - 1.0) * scalePhaseProgress - } - - // 倾斜胶卷透明度 - private var tiltedOpacity: Double { - guard animationProgress >= constantSpeedEnd, animationProgress < scaleStartEnd else { - return 0.8 - } - - let fadeProgress = (animationProgress - constantSpeedEnd) / (scaleStartEnd - constantSpeedEnd) - return 0.8 * (1.0 - fadeProgress) - } - - // 控制倾斜胶卷显示 - private var showTiltedReels: Bool { - animationProgress < scaleStartEnd - } -} - -// MARK: - 胶卷组件 - -struct FilmReelView: View { - let images: [String] - - var body: some View { - HStack(spacing: 12) { - ForEach(images.indices, id: \.self) { index in - FilmFrameView(imageName: images[index]) - } - } - } -} - -struct FilmFrameView: View { - let imageName: String - - var body: some View { - ZStack { - // 胶卷边框 - RoundedRectangle(cornerRadius: 4) - .stroke(Color.gray, lineWidth: 2) - .background(Color(red: 0.15, green: 0.15, blue: 0.15)) - - // 帧内容 - Rectangle() - .fill(gradientColor) - .cornerRadius(2) - .padding(2) - - // 帧标识 - Text(imageName) - .foregroundColor(.white) - .font(.caption2) - } - .frame(width: 90, height: 130) - // 胶卷孔洞 - .overlay( - HStack { - VStack(spacing: 6) { - ForEach(0..<6) { _ in - Circle() - .frame(width: 6, height: 6) - .foregroundColor(.gray) - } - } - Spacer() - VStack(spacing: 6) { - ForEach(0..<6) { _ in - Circle() - .frame(width: 6, height: 6) - .foregroundColor(.gray) - } - } - } - ) - } - - private var gradientColor: LinearGradient { - if imageName.hasPrefix("film1") { - return LinearGradient(gradient: Gradient(colors: [.blue, .indigo]), startPoint: .topLeading, endPoint: .bottomTrailing) - } else if imageName.hasPrefix("film2") { - return LinearGradient(gradient: Gradient(colors: [.yellow, .orange]), startPoint: .topLeading, endPoint: .bottomTrailing) - } else { - return LinearGradient(gradient: Gradient(colors: [.purple, .pink]), startPoint: .topLeading, endPoint: .bottomTrailing) - } - } -} - -// 预览 -struct FilmAnimation_Previews: PreviewProvider { - static var previews: some View { - FilmAnimation() - } -} - diff --git a/wake/View/Blind/JoinModal.swift b/wake/View/Subscribe/JoinModal.swift similarity index 100% rename from wake/View/Blind/JoinModal.swift rename to wake/View/Subscribe/JoinModal.swift