From 239485f6788ec880183177fe4f92cdf7f80a98bc Mon Sep 17 00:00:00 2001 From: jinyaqiu Date: Tue, 26 Aug 2025 17:55:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9A=82=E6=8F=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wake.xcodeproj/project.pbxproj | 17 ++++ .../xcshareddata/swiftpm/Package.resolved | 11 ++- wake/Assets/Svg/Light1.svg | 45 ++++++++++ wake/Assets/Svg/Light2.svg | 48 +++++++++++ wake/Assets/Svg/Light3.svg | 45 ++++++++++ wake/ContentView.swift | 24 +++--- wake/View/Blind/Card.swift | 84 +++++++++++++++++++ 7 files changed, 263 insertions(+), 11 deletions(-) create mode 100644 wake/Assets/Svg/Light1.svg create mode 100644 wake/Assets/Svg/Light2.svg create mode 100644 wake/Assets/Svg/Light3.svg create mode 100644 wake/View/Blind/Card.swift diff --git a/wake.xcodeproj/project.pbxproj b/wake.xcodeproj/project.pbxproj index 746286c..6b1cc28 100644 --- a/wake.xcodeproj/project.pbxproj +++ b/wake.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ AB8773632E4E04400071CB53 /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = AB8773622E4E040E0071CB53 /* .gitignore */; }; + ABC150C12E5DB39A00A1F970 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = ABC150C02E5DB39A00A1F970 /* Lottie */; }; ABE8998E2E533A7100CD7BA6 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = ABE8998D2E533A7100CD7BA6 /* Alamofire */; }; /* End PBXBuildFile section */ @@ -57,6 +58,7 @@ buildActionMask = 2147483647; files = ( ABE8998E2E533A7100CD7BA6 /* Alamofire in Frameworks */, + ABC150C12E5DB39A00A1F970 /* Lottie in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -111,6 +113,7 @@ name = wake; packageProductDependencies = ( ABE8998D2E533A7100CD7BA6 /* Alamofire */, + ABC150C02E5DB39A00A1F970 /* Lottie */, ); productName = wake; productReference = ABB4E2082E4B75D900660198 /* wake.app */; @@ -142,6 +145,7 @@ minimizedProjectReferenceProxies = 1; packageReferences = ( ABE8998C2E533A7100CD7BA6 /* XCRemoteSwiftPackageReference "Alamofire" */, + ABC150BF2E5DB39A00A1F970 /* XCRemoteSwiftPackageReference "lottie-spm" */, ); preferredProjectObjectVersion = 77; productRefGroup = ABB4E2092E4B75D900660198 /* Products */; @@ -382,6 +386,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + ABC150BF2E5DB39A00A1F970 /* XCRemoteSwiftPackageReference "lottie-spm" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/airbnb/lottie-spm.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.5.2; + }; + }; ABE8998C2E533A7100CD7BA6 /* XCRemoteSwiftPackageReference "Alamofire" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/Alamofire/Alamofire.git"; @@ -393,6 +405,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + ABC150C02E5DB39A00A1F970 /* Lottie */ = { + isa = XCSwiftPackageProductDependency; + package = ABC150BF2E5DB39A00A1F970 /* XCRemoteSwiftPackageReference "lottie-spm" */; + productName = Lottie; + }; ABE8998D2E533A7100CD7BA6 /* Alamofire */ = { isa = XCSwiftPackageProductDependency; package = ABE8998C2E533A7100CD7BA6 /* XCRemoteSwiftPackageReference "Alamofire" */; diff --git a/wake.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/wake.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 8c6ea58..c7f1eae 100644 --- a/wake.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/wake.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "e8f130fe30ac6cdc940ef06ee1e8535e9f46ffee6aeead1722b9525562f6ce08", + "originHash" : "0e95cd18402f001189cea942918f7d0c4c8b04175c6c482029650c892d28d55a", "pins" : [ { "identity" : "alamofire", @@ -9,6 +9,15 @@ "revision" : "513364f870f6bfc468f9d2ff0a95caccc10044c5", "version" : "5.10.2" } + }, + { + "identity" : "lottie-spm", + "kind" : "remoteSourceControl", + "location" : "https://github.com/airbnb/lottie-spm.git", + "state" : { + "revision" : "04f2fd18cc9404a0a0917265a449002674f24ec9", + "version" : "4.5.2" + } } ], "version" : 3 diff --git a/wake/Assets/Svg/Light1.svg b/wake/Assets/Svg/Light1.svg new file mode 100644 index 0000000..5cdf5f1 --- /dev/null +++ b/wake/Assets/Svg/Light1.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wake/Assets/Svg/Light2.svg b/wake/Assets/Svg/Light2.svg new file mode 100644 index 0000000..defe932 --- /dev/null +++ b/wake/Assets/Svg/Light2.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wake/Assets/Svg/Light3.svg b/wake/Assets/Svg/Light3.svg new file mode 100644 index 0000000..b243664 --- /dev/null +++ b/wake/Assets/Svg/Light3.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wake/ContentView.swift b/wake/ContentView.swift index f52d3e5..a2e4978 100644 --- a/wake/ContentView.swift +++ b/wake/ContentView.swift @@ -19,6 +19,9 @@ struct ContentView: View { @State private var showSettings = false // 控制设置页面显示 @State private var contentOffset: CGFloat = 0 // 内容偏移量 @State private var showLogin = false + @State private var animateGradient = false + + let timer = Timer.publish(every: 0.02, on: .main, in: .common).autoconnect() // 获取模型上下文 @Environment(\.modelContext) private var modelContext @@ -98,21 +101,22 @@ struct ContentView: View { .padding(.horizontal) // 盲盒 // 添加SVG背景图片 - GeometryReader { geometry in - Color.clear.overlay( - SVGImage(svgName: "BlindBg") - .frame( - width: UIScreen.main.bounds.width * 1.8, - height: geometry.size.height - ) - .position(x: geometry.size.width / 2, y: geometry.size.height / 2), - alignment: .center - ) + ZStack { + // 背景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) + // AE 动画 通过lottie实现 + } .frame( maxWidth: .infinity, maxHeight: UIScreen.main.bounds.height * 0.65 ) + .clipped() // 打开 Button(action: showUserProfile) { Text("Go to Buy") diff --git a/wake/View/Blind/Card.swift b/wake/View/Blind/Card.swift new file mode 100644 index 0000000..f5f8e41 --- /dev/null +++ b/wake/View/Blind/Card.swift @@ -0,0 +1,84 @@ +import SwiftUI + +struct CustomLightSequenceAnimation: View { + // 核心循环序列:按"123321123321"规律定义基础单元 + private let baseSequence: [Int] = [1, 2, 3, 3, 2, 1, 1, 2, 3, 3, 2, 1] + @State private var currentLight: Int = 1 // 当前显示的图片序号 + @State private var sequenceIndex: Int = 0 // 当前在序列中的索引 + + // 淡入淡出透明度控制(确保切换丝滑) + @State private var currentOpacity: CGFloat = 1.0 + @State private var nextOpacity: CGFloat = 0.0 + + // 尺寸参数(适配正方形卡片) + private let screenWidth = UIScreen.main.bounds.width + private let squareSize: CGFloat + private let imageSize: CGFloat + + init() { + self.squareSize = screenWidth * 1.8 // 正方形背景尺寸 + self.imageSize = squareSize / 3 // 光束卡片尺寸(1/3背景大小) + } + + // 卡片中心位置(固定,确保摆正居中) + private var centerPosition: CGPoint { + CGPoint(x: screenWidth / 2, y: squareSize * 0.325) + } + + var body: some View { + ZStack { + // 底部背景(正方形) + SVGImage(svgName: "BlindBg") + .frame(width: squareSize, height: squareSize) + .position(centerPosition) + + // 当前显示的光束卡片(摆正状态) + SVGImage(svgName: "Light\(currentLight)") + .frame(width: imageSize, height: imageSize) + .position(centerPosition) + .opacity(currentOpacity) + + // 下一张待显示的光束卡片(提前加载,摆正状态) + SVGImage(svgName: "Light\(nextLight)") + .frame(width: imageSize, height: imageSize) + .position(centerPosition) + .opacity(nextOpacity) + } + .onAppear { + startLoopAnimation() + } + } + + // 计算下一张卡片序号(基于当前索引循环) + private var nextLight: Int { + let nextIdx = (sequenceIndex + 1) % baseSequence.count + return baseSequence[nextIdx] + } + + // 启动循环切换动画 + private func startLoopAnimation() { + // 每1.2秒切换一次(可调整速度) + Timer.scheduledTimer(withTimeInterval: 1.2, repeats: true) { _ in + // 0.5秒淡入淡出过渡(丝滑无卡顿) + withAnimation(Animation.easeInOut(duration: 0.5)) { + currentOpacity = 0.0 + nextOpacity = 1.0 + } + + // 动画完成后更新状态(确保顺序无偏差) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + currentLight = nextLight + sequenceIndex = (sequenceIndex + 1) % baseSequence.count + currentOpacity = 1.0 + nextOpacity = 0.0 + } + } + } +} + +// 预览 +struct CustomLightSequenceAnimation_Previews: PreviewProvider { + static var previews: some View { + CustomLightSequenceAnimation() + } +} \ No newline at end of file