wake-ios/wake/SharedUI/Graphics/Triangle.swift

202 lines
6.0 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import SwiftUI
///
public enum TriangleDirection: Equatable {
case up
case down
case left
case right
}
/// (inset) Shape
struct TriangleShape: InsettableShape {
var direction: TriangleDirection = .up
var insetAmount: CGFloat = 0
var animatableData: CGFloat {
get { insetAmount }
set { insetAmount = newValue }
}
func inset(by amount: CGFloat) -> TriangleShape {
var copy = self
copy.insetAmount += amount
return copy
}
func path(in rect: CGRect) -> Path {
let r = rect.insetBy(dx: insetAmount, dy: insetAmount)
//
let p1: CGPoint
let p2: CGPoint
let p3: CGPoint
switch direction {
case .up:
p1 = CGPoint(x: r.midX, y: r.minY)
p2 = CGPoint(x: r.maxX, y: r.maxY)
p3 = CGPoint(x: r.minX, y: r.maxY)
case .down:
p1 = CGPoint(x: r.midX, y: r.maxY)
p2 = CGPoint(x: r.minX, y: r.minY)
p3 = CGPoint(x: r.maxX, y: r.minY)
case .left:
p1 = CGPoint(x: r.minX, y: r.midY)
p2 = CGPoint(x: r.maxX, y: r.minY)
p3 = CGPoint(x: r.maxX, y: r.maxY)
case .right:
p1 = CGPoint(x: r.maxX, y: r.midY)
p2 = CGPoint(x: r.minX, y: r.maxY)
p3 = CGPoint(x: r.minX, y: r.minY)
}
var path = Path()
path.move(to: p1)
path.addLine(to: p2)
path.addLine(to: p3)
path.closeSubpath()
return path
}
}
/// 便
///
/// TriangleView(width: 20, height: 18, direction: .up, color: .black, stroke: .white, lineWidth: 2)
public struct TriangleView: View {
public var width: CGFloat
public var height: CGFloat
public var direction: TriangleDirection
public var color: Color
public var stroke: Color? = nil
public var lineWidth: CGFloat = 1
public var rotation: Angle = .degrees(0)
public init(
width: CGFloat,
height: CGFloat,
direction: TriangleDirection = .up,
color: Color = .black,
stroke: Color? = nil,
lineWidth: CGFloat = 1,
rotation: Angle = .degrees(0)
) {
self.width = width
self.height = height
self.direction = direction
self.color = color
self.stroke = stroke
self.lineWidth = lineWidth
self.rotation = rotation
}
public var body: some View {
let shape = TriangleShape(direction: direction)
Group {
if let stroke = stroke, lineWidth > 0 {
shape.fill(color)
.overlay(
shape.stroke(stroke, lineWidth: lineWidth)
)
} else {
shape.fill(color)
}
}
.frame(width: width, height: height)
.rotationEffect(rotation)
.accessibilityHidden(true)
}
}
///
public struct TriangleRow: View {
public var count: Int
public var itemSize: CGSize
public var direction: TriangleDirection
public var color: Color
public var spacing: CGFloat
public var rotation: Angle
public init(
count: Int,
itemSize: CGSize,
direction: TriangleDirection = .up,
color: Color = .black,
spacing: CGFloat = 8,
rotation: Angle = .degrees(0)
) {
self.count = count
self.itemSize = itemSize
self.direction = direction
self.color = color
self.spacing = spacing
self.rotation = rotation
}
public var body: some View {
HStack(spacing: spacing) {
ForEach(0..<(max(0, count)), id: \.self) { _ in
TriangleView(
width: itemSize.width,
height: itemSize.height,
direction: direction,
color: color,
rotation: rotation
)
}
}
.accessibilityElement(children: .ignore)
}
}
#if DEBUG
struct Triangle_Previews: PreviewProvider {
static var previews: some View {
Group {
//
TriangleView(width: 24, height: 20, direction: .up, color: .black, stroke: .white, lineWidth: 2)
.padding()
.previewDisplayName("Single Up")
TriangleView(width: 24, height: 20, direction: .right, color: .orange)
.padding()
.background(Color(.systemGroupedBackground))
.previewDisplayName("Single Right")
TriangleView(width: 24, height: 20, direction: .up, color: .blue, rotation: .degrees(132))
.padding()
.previewDisplayName("Rotated 132")
//
ZStack {
Color(white: 0.97)
HStack {
RoundedRectangle(cornerRadius: 18, style: .continuous)
.fill(.white)
.shadow(color: Color.black.opacity(0.06), radius: 6, x: 0, y: 2)
.overlay(
HStack {
TriangleRow(
count: 6,
itemSize: CGSize(width: 16, height: 14),
direction: .up,
color: .black,
spacing: 6,
rotation: .degrees(180)
)
}
.padding(.horizontal, 18)
)
.frame(height: 64)
.padding()
}
}
.previewDisplayName("Row")
}
.previewLayout(.sizeThatFits)
}
}
#endif