This commit is contained in:
Junhui Chen 2025-09-10 19:25:33 +08:00
parent 17dc5bbe5e
commit 5ca76c4e5b
9 changed files with 238 additions and 93 deletions

View File

@ -20,6 +20,52 @@ struct CustomLightSequenceAnimation: View {
self.imageSize = squareSize / 3 // 1/3 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 { private var centerPosition: CGPoint {
CGPoint(x: screenWidth / 2, y: squareSize * 0.325) CGPoint(x: screenWidth / 2, y: squareSize * 0.325)
@ -27,53 +73,13 @@ struct CustomLightSequenceAnimation: View {
var body: some View { var body: some View {
ZStack { ZStack {
// // SwiftUI
SVGImage(svgName: "BlindBg") CardBlindBackground()
.frame(width: squareSize, height: squareSize) .frame(width: squareSize, height: squareSize)
.position(centerPosition) .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
}
}
}
} }
// //

View File

@ -384,11 +384,9 @@ struct BlindBoxView: View {
// //
ZStack { ZStack {
// 1. SVG // 1. SwiftUI
if !showScalingOverlay { if !showScalingOverlay {
SVGImage(svgName: "BlindBg", contentMode: .fit) BlindBackground()
// .position(x: UIScreen.main.bounds.width / 2,
// y: UIScreen.main.bounds.height * 0.325)
.opacity(showScalingOverlay ? 0 : 1) .opacity(showScalingOverlay ? 0 : 1)
.animation(.easeOut(duration: 1.5), value: showScalingOverlay) .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() { private func hideSettings() {
withAnimation(.spring(response: 0.6, dampingFraction: 0.8)) { withAnimation(.spring(response: 0.6, dampingFraction: 0.8)) {

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,4 +1,5 @@
import SwiftUI import SwiftUI
import UIKit
struct AboutUsView: View { struct AboutUsView: View {
// MARK: - Properties // MARK: - Properties
@ -25,8 +26,19 @@ struct AboutUsView: View {
VStack(spacing: 0) { VStack(spacing: 0) {
// IP Address Section // IP Address Section
VStack(spacing: 12) { VStack(spacing: 12) {
SVGImage(svgName: "AboutIP") if let icon = primaryAppIconUIImage() {
Image(uiImage: icon)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 102, height: 102) .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) .frame(maxWidth: .infinity)
.padding(.vertical, 32) .padding(.vertical, 32)
@ -113,6 +125,18 @@ struct AboutUsView: View {
} }
// MARK: - Private Methods // 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? { private func getIPAddress() -> String? {
var address: String? var address: String?
var ifaddr: UnsafeMutablePointer<ifaddrs>? var ifaddr: UnsafeMutablePointer<ifaddrs>?

View File

@ -59,9 +59,16 @@ public struct AvatarPicker: View {
) )
.scaleEffect(scaleFactor) .scaleEffect(scaleFactor)
} else { } else {
// Default SVG avatar with animated dashed border // SwiftUI + 线 + 线
SVGImageHtml(svgName: "IP") ZStack {
RoundedRectangle(cornerRadius: 20)
.fill(Color.white)
.frame(width: 225, height: 225) .frame(width: 225, height: 225)
Image(systemName: "plus")
.font(.system(size: 32, weight: .bold))
.foregroundColor(.black)
}
.scaleEffect(scaleFactor) .scaleEffect(scaleFactor)
.contentShape(Rectangle()) .contentShape(Rectangle())
.clipShape(RoundedRectangle(cornerRadius: 20 * scaleFactor)) .clipShape(RoundedRectangle(cornerRadius: 20 * scaleFactor))

View File

@ -19,16 +19,7 @@ struct JoinModal: View {
// Modal content // Modal content
if isPresented { if isPresented {
VStack(spacing: 0) { 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) { VStack(spacing: 0) {
// Close button on the right // Close button on the right
HStack { HStack {
@ -59,7 +50,7 @@ struct JoinModal: View {
// List content // List content
VStack (alignment: .leading) { VStack (alignment: .leading) {
HStack { HStack {
SVGImage(svgName: "JoinList") JoinListMark()
.frame(width: 32, height: 32) .frame(width: 32, height: 32)
HStack (alignment: .top){ HStack (alignment: .top){
Text("Unlimited") Text("Unlimited")
@ -73,7 +64,7 @@ struct JoinModal: View {
.padding(.vertical, 12) .padding(.vertical, 12)
.padding(.leading,12) .padding(.leading,12)
HStack (alignment: .center) { HStack (alignment: .center) {
SVGImage(svgName: "JoinList") JoinListMark()
.frame(width: 32, height: 32) .frame(width: 32, height: 32)
VStack (alignment: .leading,spacing: 4) { VStack (alignment: .leading,spacing: 4) {
HStack { HStack {
@ -93,7 +84,7 @@ struct JoinModal: View {
.padding(.leading,12) .padding(.leading,12)
HStack(alignment: .top) { HStack(alignment: .top) {
SVGImage(svgName: "JoinList") JoinListMark()
.frame(width: 32, height: 32) .frame(width: 32, height: 32)
VStack (alignment: .leading,spacing: 4) { VStack (alignment: .leading,spacing: 4) {
HStack { 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 { struct JoinModal_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
JoinModal(isPresented: .constant(true)) JoinModal(isPresented: .constant(true))