chore
This commit is contained in:
parent
17dc5bbe5e
commit
5ca76c4e5b
@ -19,6 +19,52 @@ struct CustomLightSequenceAnimation: View {
|
|||||||
self.squareSize = screenWidth * 1.8 // 正方形背景尺寸
|
self.squareSize = screenWidth * 1.8 // 正方形背景尺寸
|
||||||
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 {
|
||||||
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 预览
|
// 预览
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
@ -665,6 +663,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() {
|
||||||
|
|||||||
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 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() {
|
||||||
.frame(width: 102, height: 102)
|
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)
|
.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>?
|
||||||
|
|||||||
@ -59,28 +59,35 @@ public struct AvatarPicker: View {
|
|||||||
)
|
)
|
||||||
.scaleEffect(scaleFactor)
|
.scaleEffect(scaleFactor)
|
||||||
} else {
|
} else {
|
||||||
// Default SVG avatar with animated dashed border
|
// SwiftUI 占位:白底 + 虚线边框 + 居中加号(带虚线动画)
|
||||||
SVGImageHtml(svgName: "IP")
|
ZStack {
|
||||||
.frame(width: 225, height: 225)
|
RoundedRectangle(cornerRadius: 20)
|
||||||
.scaleEffect(scaleFactor)
|
.fill(Color.white)
|
||||||
.contentShape(Rectangle())
|
.frame(width: 225, height: 225)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 20 * scaleFactor))
|
|
||||||
.overlay(
|
Image(systemName: "plus")
|
||||||
RoundedRectangle(cornerRadius: 20)
|
.font(.system(size: 32, weight: .bold))
|
||||||
.stroke(style: StrokeStyle(
|
.foregroundColor(.black)
|
||||||
lineWidth: borderWidth,
|
}
|
||||||
lineCap: .round,
|
.scaleEffect(scaleFactor)
|
||||||
dash: [12, 8],
|
.contentShape(Rectangle())
|
||||||
dashPhase: isAnimating ? 40 : 0
|
.clipShape(RoundedRectangle(cornerRadius: 20 * scaleFactor))
|
||||||
))
|
.overlay(
|
||||||
.foregroundColor(Color.themePrimary)
|
RoundedRectangle(cornerRadius: 20)
|
||||||
.scaleEffect(scaleFactor)
|
.stroke(style: StrokeStyle(
|
||||||
)
|
lineWidth: borderWidth,
|
||||||
.onAppear {
|
lineCap: .round,
|
||||||
withAnimation(Animation.linear(duration: 1.5).repeatForever(autoreverses: false)) {
|
dash: [12, 8],
|
||||||
isAnimating = true
|
dashPhase: isAnimating ? 40 : 0
|
||||||
}
|
))
|
||||||
|
.foregroundColor(Color.themePrimary)
|
||||||
|
.scaleEffect(scaleFactor)
|
||||||
|
)
|
||||||
|
.onAppear {
|
||||||
|
withAnimation(Animation.linear(duration: 1.5).repeatForever(autoreverses: false)) {
|
||||||
|
isAnimating = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload indicator
|
// Upload indicator
|
||||||
|
|||||||
@ -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 {
|
||||||
@ -57,9 +48,9 @@ struct JoinModal: View {
|
|||||||
}
|
}
|
||||||
.padding(.vertical, 12)
|
.padding(.vertical, 12)
|
||||||
// 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 {
|
||||||
@ -91,9 +82,9 @@ struct JoinModal: View {
|
|||||||
}
|
}
|
||||||
.padding(.vertical, 12)
|
.padding(.vertical, 12)
|
||||||
.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 {
|
||||||
@ -123,7 +114,7 @@ struct JoinModal: View {
|
|||||||
}
|
}
|
||||||
.padding(.top, 12)
|
.padding(.top, 12)
|
||||||
.padding(.leading,12)
|
.padding(.leading,12)
|
||||||
HStack {
|
HStack {
|
||||||
Spacer() // This will push the button to the right
|
Spacer() // This will push the button to the right
|
||||||
Button(action: {
|
Button(action: {
|
||||||
// 点击跳转到会员页面
|
// 点击跳转到会员页面
|
||||||
@ -164,7 +155,7 @@ struct JoinModal: View {
|
|||||||
HStack(alignment: .center) {
|
HStack(alignment: .center) {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
// Action for Terms of Service
|
// Action for Terms of Service
|
||||||
if let url = URL(string: "https://memorywake.com/privacy-policy") {
|
if let url = URL(string: "https://memorywake.com/privacy-policy") {
|
||||||
UIApplication.shared.open(url)
|
UIApplication.shared.open(url)
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
@ -173,11 +164,11 @@ struct JoinModal: View {
|
|||||||
.foregroundColor(.themeTextMessage)
|
.foregroundColor(.themeTextMessage)
|
||||||
.underline() // Add underline
|
.underline() // Add underline
|
||||||
}
|
}
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.fill(Color.gray.opacity(0.5))
|
.fill(Color.gray.opacity(0.5))
|
||||||
.frame(width: 1, height: 16)
|
.frame(width: 1, height: 16)
|
||||||
.padding(.vertical, 4)
|
.padding(.vertical, 4)
|
||||||
Button(action: {
|
Button(action: {
|
||||||
// 打开网页
|
// 打开网页
|
||||||
if let url = URL(string: "https://memorywake.com/privacy-policy") {
|
if let url = URL(string: "https://memorywake.com/privacy-policy") {
|
||||||
UIApplication.shared.open(url)
|
UIApplication.shared.open(url)
|
||||||
@ -188,13 +179,13 @@ struct JoinModal: View {
|
|||||||
.foregroundColor(.themeTextMessage)
|
.foregroundColor(.themeTextMessage)
|
||||||
.underline() // Add underline
|
.underline() // Add underline
|
||||||
}
|
}
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.fill(Color.gray.opacity(0.5))
|
.fill(Color.gray.opacity(0.5))
|
||||||
.frame(width: 1, height: 16)
|
.frame(width: 1, height: 16)
|
||||||
.padding(.vertical, 4)
|
.padding(.vertical, 4)
|
||||||
Button(action: {
|
Button(action: {
|
||||||
// Action for Restore Purchase
|
// Action for Restore Purchase
|
||||||
if let url = URL(string: "https://memorywake.com/privacy-policy") {
|
if let url = URL(string: "https://memorywake.com/privacy-policy") {
|
||||||
UIApplication.shared.open(url)
|
UIApplication.shared.open(url)
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
@ -206,8 +197,8 @@ struct JoinModal: View {
|
|||||||
}
|
}
|
||||||
.padding(.bottom, 24)
|
.padding(.bottom, 24)
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 16)
|
.padding(.horizontal, 16)
|
||||||
}
|
}
|
||||||
.background(Color.white)
|
.background(Color.white)
|
||||||
.cornerRadius(20, corners: [.topLeft, .topRight])
|
.cornerRadius(20, corners: [.topLeft, .topRight])
|
||||||
@ -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))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user