diff --git a/wake/Features/BlindBox/Components/Card.swift b/wake/Features/BlindBox/Components/Card.swift index f057b6f..34ef2a5 100644 --- a/wake/Features/BlindBox/Components/Card.swift +++ b/wake/Features/BlindBox/Components/Card.swift @@ -19,52 +19,6 @@ struct CustomLightSequenceAnimation: View { self.squareSize = screenWidth * 1.8 // 正方形背景尺寸 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 { @@ -82,9 +36,57 @@ private struct CardBlindBackground: View { } + +// MARK: - SwiftUI 背景重绘(方形版本) +private struct CardBlindBackground: View { + var body: some View { + GeometryReader { geo in + let w = geo.size.width + let h = geo.size.height + ZStack { + // 主背景卡片(方形) + ScoopRoundedRect(cornerRadius: 20, scoopDepth: 20, scoopHalfWidth: 90, scoopCenterX: 0.5, convexDown: true, flatHalfWidth: 60) + .fill(Theme.Colors.primary) + .shadow(color: .black.opacity(0.08), radius: 12, y: 6) + .padding() + + // 左上光斑 + // 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) + } + } + } +} + // 预览 struct CustomLightSequenceAnimation_Previews: PreviewProvider { static var previews: some View { CustomLightSequenceAnimation() } +} + +// 预览 +struct CardBlindBackground_Previews: PreviewProvider { + static var previews: some View { + CardBlindBackground() + .frame(width: 400, height: 600) + } } \ No newline at end of file diff --git a/wake/SharedUI/Graphics/ScoopRoundedRect.swift b/wake/SharedUI/Graphics/ScoopRoundedRect.swift new file mode 100644 index 0000000..de3cfcf --- /dev/null +++ b/wake/SharedUI/Graphics/ScoopRoundedRect.swift @@ -0,0 +1,110 @@ +import SwiftUI + +/// 顶部带“内凹”弧形的连续圆角矩形 +struct ScoopRoundedRect: Shape { + var cornerRadius: CGFloat = 20 + /// 凹凸的“深度”:>0 向下凸(更接近你截图的效果),<0 为向上凹 + var scoopDepth: CGFloat = 10 + /// 半宽:控制水平占据范围(越大越“平缓”) + var scoopHalfWidth: CGFloat = 18 + /// 相对位置(0~1,0.5 正中) + var scoopCenterX: CGFloat = 0.33 + /// 是否向下凸;为 false 时表现为向上“凹”(与 notch 类似) + var convexDown: Bool = true + /// 凹陷/鼓包底部的“平底”半宽(0 为无平底) + var flatHalfWidth: CGFloat = 8 + + func path(in rect: CGRect) -> Path { + let r = min(cornerRadius, min(rect.width, rect.height) * 0.5) + let topY = rect.minY + + // 约束中心与范围,避免穿出圆角 + let minX = rect.minX + r + let maxX = rect.maxX - r + let centerX = rect.minX + rect.width * scoopCenterX + let hw = min(scoopHalfWidth, (maxX - minX) * 0.45) + let flatHW = max(0, min(flatHalfWidth, hw * 0.8)) + let shoulder = max(1, hw - flatHW) // 两侧曲线的水平长度 + let startX = max(minX, centerX - (flatHW + shoulder)) + let endX = min(maxX, centerX + (flatHW + shoulder)) + let leftFlatX = max(minX, centerX - flatHW) + let rightFlatX = min(maxX, centerX + flatHW) + let depth = (convexDown ? 1 : -1) * scoopDepth + + // 左曲线:P0 -> Lf(水平);右曲线:Rf -> P3(水平);Lf~Rf 之间是一段水平直线 + let P0 = CGPoint(x: startX, y: topY) + let Lf = CGPoint(x: leftFlatX, y: topY + depth) + let Rf = CGPoint(x: rightFlatX, y: topY + depth) + let P3 = CGPoint(x: endX, y: topY) + + // 使用圆角近似系数控制手柄长度(基于 shoulder) + let k = shoulder * 0.5522847498 + let C1 = CGPoint(x: P0.x + k, y: P0.y) // P0 水平切线 + let C2 = CGPoint(x: Lf.x - k, y: Lf.y) // Lf 水平切线 + let C3 = CGPoint(x: Rf.x + k, y: Rf.y) // Rf 水平切线 + let C4 = CGPoint(x: P3.x - k, y: P3.y) // P3 水平切线 + + var p = Path() + // 顶部从左上圆角起 + p.move(to: CGPoint(x: rect.minX + r, y: topY)) + // 左侧直线到凹/凸开始 + p.addLine(to: P0) + // 左侧进入曲线 + p.addCurve(to: Lf, control1: C1, control2: C2) + // 平底水平直线 + p.addLine(to: Rf) + // 右侧离开曲线 + p.addCurve(to: P3, control1: C3, control2: C4) + // 顶部到右上圆角 + p.addLine(to: CGPoint(x: rect.maxX - r, y: topY)) + // 右上圆角 + p.addQuadCurve(to: CGPoint(x: rect.maxX, y: rect.minY + r), + control: CGPoint(x: rect.maxX, y: rect.minY)) + // 右侧直线到右下圆角 + p.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - r)) + // 右下圆角 + p.addQuadCurve(to: CGPoint(x: rect.maxX - r, y: rect.maxY), + control: CGPoint(x: rect.maxX, y: rect.maxY)) + // 底边到左下圆角 + p.addLine(to: CGPoint(x: rect.minX + r, y: rect.maxY)) + // 左下圆角 + p.addQuadCurve(to: CGPoint(x: rect.minX, y: rect.maxY - r), + control: CGPoint(x: rect.minX, y: rect.maxY)) + // 左侧到左上圆角 + p.addLine(to: CGPoint(x: rect.minX, y: rect.minY + r)) + // 左上圆角 + p.addQuadCurve(to: CGPoint(x: rect.minX + r, y: rect.minY), + control: CGPoint(x: rect.minX, y: rect.minY)) + p.closeSubpath() + return p + } +} + +struct ScoopRoundedRect_Previews: PreviewProvider { + static var previews: some View { + VStack(spacing: 30) { + // 向下“鼓包”且底部有平直段 + ScoopRoundedRect(cornerRadius: 24, scoopDepth: 8, scoopHalfWidth: 26, scoopCenterX: 0.25, convexDown: true, flatHalfWidth: 12) + .fill(Color.orange) + .frame(height: 140) + .shadow(color: .black.opacity(0.08), radius: 12, y: 6) + .padding() + + // 中央更深、更宽,平底更宽 + ScoopRoundedRect(cornerRadius: 28, scoopDepth: 12, scoopHalfWidth: 36, scoopCenterX: 0.5, convexDown: true, flatHalfWidth: 18) + .fill(Color.orange) + .frame(height: 140) + .shadow(color: .black.opacity(0.08), radius: 12, y: 6) + .padding() + + // 作为对比:向上“凹陷”的 notch,带平底 + ScoopRoundedRect(cornerRadius: 24, scoopDepth: 10, scoopHalfWidth: 22, scoopCenterX: 0.6, convexDown: false, flatHalfWidth: 10) + .fill(Color.orange) + .frame(height: 140) + .shadow(color: .black.opacity(0.08), radius: 12, y: 6) + .padding() + } + .background(Color(white: 0.96)) + .previewLayout(.sizeThatFits) + } +} \ No newline at end of file