53 lines
1.9 KiB
Swift
53 lines
1.9 KiB
Swift
#if os(iOS)
|
|
import SwiftUI
|
|
|
|
@available(macOS 10.15, iOS 13.0, *)
|
|
struct BubbleMetric: Identifiable {
|
|
let id = UUID()
|
|
let label: String
|
|
let display: String
|
|
let valueMinutes: Double // 用于相对半径
|
|
let color: Color
|
|
}
|
|
|
|
@available(macOS 10.15, iOS 13.0, *)
|
|
struct BubbleMetricsView: View {
|
|
let metrics: [BubbleMetric]
|
|
|
|
private func radius(for v: Double, maxV: Double) -> CGFloat {
|
|
guard maxV > 0 else { return 20 }
|
|
let norm = sqrt(v / maxV)
|
|
return 30 + norm * 70
|
|
}
|
|
|
|
var body: some View {
|
|
GeometryReader { geo in
|
|
let maxV = metrics.map { $0.valueMinutes }.max() ?? 1
|
|
ZStack {
|
|
ForEach(Array(metrics.enumerated()), id: \.[1].id) { idx, m in
|
|
// 简单环形布局
|
|
let angle = Double(idx) / Double(metrics.count) * Double.pi * 2
|
|
let R = min(geo.size.width, geo.size.height) / 2 - 80
|
|
let x = geo.size.width/2 + CGFloat(cos(angle)) * R
|
|
let y = geo.size.height/2 + CGFloat(sin(angle)) * R
|
|
let r = radius(for: m.valueMinutes, maxV: maxV)
|
|
Circle()
|
|
.fill(m.color.opacity(0.75))
|
|
.frame(width: r, height: r)
|
|
.overlay(
|
|
VStack(spacing:4){
|
|
Text(m.display).font(.system(size: min(18, r*0.28))).bold().foregroundColor(.white)
|
|
Text(m.label).font(.system(size: min(14, r*0.22))).foregroundColor(.white)
|
|
}.padding(4)
|
|
)
|
|
.position(x: x, y: y)
|
|
.shadow(radius: 4, y: 2)
|
|
}
|
|
}
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
}
|
|
.frame(height: 340)
|
|
}
|
|
}
|
|
#endif
|