chore
This commit is contained in:
parent
17dc5bbe5e
commit
5ca76c4e5b
@ -20,6 +20,52 @@ struct CustomLightSequenceAnimation: View {
|
||||
self.imageSize = squareSize / 3 // 光束卡片尺寸(1/3背景大小)
|
||||
}
|
||||
|
||||
// MARK: - SwiftUI 背景重绘(方形版本)
|
||||
private struct CardBlindBackground: View {
|
||||
var body: some View {
|
||||
GeometryReader { geo in
|
||||
let w = geo.size.width
|
||||
let h = geo.size.height
|
||||
ZStack {
|
||||
// 主背景卡片(方形)
|
||||
RoundedRectangle(cornerRadius: 28)
|
||||
.fill(
|
||||
LinearGradient(
|
||||
colors: [Color.white, Color.white.opacity(0.96)],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
)
|
||||
.shadow(color: Color.black.opacity(0.06), radius: 16, x: 0, y: 8)
|
||||
.frame(width: w * 0.88, height: h * 0.88)
|
||||
.position(x: w / 2, y: h / 2)
|
||||
|
||||
// 左上光斑
|
||||
Circle()
|
||||
.fill(Color.themePrimary.opacity(0.18))
|
||||
.blur(radius: 40)
|
||||
.frame(width: min(w, h) * 0.35, height: min(w, h) * 0.35)
|
||||
.position(x: w * 0.25, y: h * 0.25)
|
||||
|
||||
// 右下光斑
|
||||
Circle()
|
||||
.fill(Color.orange.opacity(0.14))
|
||||
.blur(radius: 50)
|
||||
.frame(width: min(w, h) * 0.40, height: min(w, h) * 0.40)
|
||||
.position(x: w * 0.75, y: h * 0.75)
|
||||
|
||||
// 中央高光描边
|
||||
RoundedRectangle(cornerRadius: 28)
|
||||
.stroke(Color.white.opacity(0.35), lineWidth: 1)
|
||||
.frame(width: w * 0.88, height: h * 0.88)
|
||||
.position(x: w / 2, y: h / 2)
|
||||
.blendMode(.overlay)
|
||||
.opacity(0.7)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 卡片中心位置(固定,确保摆正居中)
|
||||
private var centerPosition: CGPoint {
|
||||
CGPoint(x: screenWidth / 2, y: squareSize * 0.325)
|
||||
@ -27,53 +73,13 @@ struct CustomLightSequenceAnimation: View {
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
// 底部背景(正方形)
|
||||
SVGImage(svgName: "BlindBg")
|
||||
// 底部背景(正方形,SwiftUI 重绘)
|
||||
CardBlindBackground()
|
||||
.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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 预览
|
||||
|
||||
@ -384,11 +384,9 @@ struct BlindBoxView: View {
|
||||
|
||||
// 盲盒
|
||||
ZStack {
|
||||
// 1. 背景SVG
|
||||
// 1. 背景(SwiftUI 重绘)
|
||||
if !showScalingOverlay {
|
||||
SVGImage(svgName: "BlindBg", contentMode: .fit)
|
||||
// .position(x: UIScreen.main.bounds.width / 2,
|
||||
// y: UIScreen.main.bounds.height * 0.325)
|
||||
BlindBackground()
|
||||
.opacity(showScalingOverlay ? 0 : 1)
|
||||
.animation(.easeOut(duration: 1.5), value: showScalingOverlay)
|
||||
}
|
||||
@ -666,6 +664,52 @@ struct BlindBoxView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 盲盒背景(SwiftUI 重绘)
|
||||
private struct BlindBackground: View {
|
||||
var body: some View {
|
||||
GeometryReader { geo in
|
||||
let w = geo.size.width
|
||||
let h = geo.size.height
|
||||
ZStack {
|
||||
// 主背景卡片
|
||||
RoundedRectangle(cornerRadius: 28)
|
||||
.fill(
|
||||
LinearGradient(
|
||||
colors: [Color.white, Color.white.opacity(0.96)],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
)
|
||||
)
|
||||
.shadow(color: Color.black.opacity(0.06), radius: 16, x: 0, y: 8)
|
||||
.frame(width: min(w * 0.9, 360), height: min(h * 0.6, 260))
|
||||
.position(x: w / 2, y: h * 0.35)
|
||||
|
||||
// 左上光斑
|
||||
Circle()
|
||||
.fill(Color.themePrimary.opacity(0.18))
|
||||
.blur(radius: 40)
|
||||
.frame(width: 160, height: 160)
|
||||
.position(x: w * 0.22, y: h * 0.18)
|
||||
|
||||
// 右下光斑
|
||||
Circle()
|
||||
.fill(Color.orange.opacity(0.14))
|
||||
.blur(radius: 50)
|
||||
.frame(width: 180, height: 180)
|
||||
.position(x: w * 0.78, y: h * 0.55)
|
||||
|
||||
// 中央高光
|
||||
RoundedRectangle(cornerRadius: 28)
|
||||
.stroke(Color.white.opacity(0.35), lineWidth: 1)
|
||||
.frame(width: min(w * 0.9, 360), height: min(h * 0.6, 260))
|
||||
.position(x: w / 2, y: h * 0.35)
|
||||
.blendMode(.overlay)
|
||||
.opacity(0.7)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 隐藏设置页面
|
||||
private func hideSettings() {
|
||||
withAnimation(.spring(response: 0.6, dampingFraction: 0.8)) {
|
||||
|
||||
21
wake/Media.xcassets/IP.imageset/Contents.json
vendored
Normal file
21
wake/Media.xcassets/IP.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "IP.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
wake/Media.xcassets/IP.imageset/IP.png
vendored
Normal file
BIN
wake/Media.xcassets/IP.imageset/IP.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
21
wake/Media.xcassets/IP1.imageset/Contents.json
vendored
Normal file
21
wake/Media.xcassets/IP1.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "IP1.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
wake/Media.xcassets/IP1.imageset/IP1.png
vendored
Normal file
BIN
wake/Media.xcassets/IP1.imageset/IP1.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@ -1,4 +1,5 @@
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
struct AboutUsView: View {
|
||||
// MARK: - Properties
|
||||
@ -25,8 +26,19 @@ struct AboutUsView: View {
|
||||
VStack(spacing: 0) {
|
||||
// IP Address Section
|
||||
VStack(spacing: 12) {
|
||||
SVGImage(svgName: "AboutIP")
|
||||
if let icon = primaryAppIconUIImage() {
|
||||
Image(uiImage: icon)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 102, height: 102)
|
||||
.cornerRadius(18)
|
||||
} else {
|
||||
Image(systemName: "app.fill")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 82, height: 82)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 32)
|
||||
@ -113,6 +125,18 @@ struct AboutUsView: View {
|
||||
}
|
||||
// MARK: - Private Methods
|
||||
|
||||
private func primaryAppIconUIImage() -> UIImage? {
|
||||
// 获取主 AppIcon 名称列表中最后一个(通常是最大尺寸)
|
||||
if let iconsDictionary = Bundle.main.infoDictionary?["CFBundleIcons"] as? [String: Any],
|
||||
let primaryIconsDictionary = iconsDictionary["CFBundlePrimaryIcon"] as? [String: Any],
|
||||
let iconFiles = primaryIconsDictionary["CFBundleIconFiles"] as? [String],
|
||||
let lastIcon = iconFiles.last,
|
||||
let image = UIImage(named: lastIcon) {
|
||||
return image
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func getIPAddress() -> String? {
|
||||
var address: String?
|
||||
var ifaddr: UnsafeMutablePointer<ifaddrs>?
|
||||
|
||||
@ -59,9 +59,16 @@ public struct AvatarPicker: View {
|
||||
)
|
||||
.scaleEffect(scaleFactor)
|
||||
} else {
|
||||
// Default SVG avatar with animated dashed border
|
||||
SVGImageHtml(svgName: "IP")
|
||||
// SwiftUI 占位:白底 + 虚线边框 + 居中加号(带虚线动画)
|
||||
ZStack {
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.fill(Color.white)
|
||||
.frame(width: 225, height: 225)
|
||||
|
||||
Image(systemName: "plus")
|
||||
.font(.system(size: 32, weight: .bold))
|
||||
.foregroundColor(.black)
|
||||
}
|
||||
.scaleEffect(scaleFactor)
|
||||
.contentShape(Rectangle())
|
||||
.clipShape(RoundedRectangle(cornerRadius: 20 * scaleFactor))
|
||||
|
||||
@ -19,16 +19,7 @@ struct JoinModal: View {
|
||||
// Modal content
|
||||
if isPresented {
|
||||
VStack(spacing: 0) {
|
||||
// IP Image peeking from top
|
||||
HStack {
|
||||
// Make sure you have an image named "IP" in your assets
|
||||
SVGImageHtml(svgName: "IP1")
|
||||
.frame(width: 116, height: 65)
|
||||
.offset(x: 30)
|
||||
Spacer()
|
||||
}
|
||||
.frame(height: 65)
|
||||
|
||||
// 顶部装饰图已移除(按需求)
|
||||
VStack(spacing: 0) {
|
||||
// Close button on the right
|
||||
HStack {
|
||||
@ -59,7 +50,7 @@ struct JoinModal: View {
|
||||
// List content
|
||||
VStack (alignment: .leading) {
|
||||
HStack {
|
||||
SVGImage(svgName: "JoinList")
|
||||
JoinListMark()
|
||||
.frame(width: 32, height: 32)
|
||||
HStack (alignment: .top){
|
||||
Text("Unlimited")
|
||||
@ -73,7 +64,7 @@ struct JoinModal: View {
|
||||
.padding(.vertical, 12)
|
||||
.padding(.leading,12)
|
||||
HStack (alignment: .center) {
|
||||
SVGImage(svgName: "JoinList")
|
||||
JoinListMark()
|
||||
.frame(width: 32, height: 32)
|
||||
VStack (alignment: .leading,spacing: 4) {
|
||||
HStack {
|
||||
@ -93,7 +84,7 @@ struct JoinModal: View {
|
||||
.padding(.leading,12)
|
||||
|
||||
HStack(alignment: .top) {
|
||||
SVGImage(svgName: "JoinList")
|
||||
JoinListMark()
|
||||
.frame(width: 32, height: 32)
|
||||
VStack (alignment: .leading,spacing: 4) {
|
||||
HStack {
|
||||
@ -222,6 +213,37 @@ struct JoinModal: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SwiftUI JoinList 图标重绘
|
||||
private struct JoinListMark: View {
|
||||
var body: some View {
|
||||
ZStack {
|
||||
// 背景圆
|
||||
Circle()
|
||||
.fill(
|
||||
LinearGradient(colors: [Color.themePrimary.opacity(0.9), Color.orange.opacity(0.8)],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing)
|
||||
)
|
||||
.shadow(color: Color.black.opacity(0.12), radius: 4, x: 0, y: 2)
|
||||
|
||||
// 右指三角(白色)
|
||||
GeometryReader { geo in
|
||||
Path { path in
|
||||
let w = geo.size.width
|
||||
let h = geo.size.height
|
||||
path.move(to: CGPoint(x: w*0.42, y: h*0.30))
|
||||
path.addLine(to: CGPoint(x: w*0.70, y: h*0.50))
|
||||
path.addLine(to: CGPoint(x: w*0.42, y: h*0.70))
|
||||
path.closeSubpath()
|
||||
}
|
||||
.fill(Color.white)
|
||||
.opacity(0.95)
|
||||
}
|
||||
.padding(8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct JoinModal_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
JoinModal(isPresented: .constant(true))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user