85 lines
2.6 KiB
Swift
85 lines
2.6 KiB
Swift
import SwiftUI
|
|
|
|
public struct TrafficLineChart: View {
|
|
private let data: [CGFloat]
|
|
private let lineColor: Color
|
|
private let gridColor: Color
|
|
private let chartHeight: CGFloat
|
|
|
|
public init(
|
|
data: [CGFloat],
|
|
lineColor: Color = .primary,
|
|
gridColor: Color = Color.secondary.opacity(0.3),
|
|
chartHeight: CGFloat = 60
|
|
) {
|
|
self.data = data
|
|
self.lineColor = lineColor
|
|
self.gridColor = gridColor
|
|
self.chartHeight = chartHeight
|
|
}
|
|
|
|
public var body: some View {
|
|
Canvas { context, size in
|
|
let width = size.width
|
|
let height = size.height
|
|
let maxValue = max((data.max() ?? 1) * 1.2, 1)
|
|
let pointCount = data.count
|
|
|
|
drawGrid(context: context, width: width, height: height)
|
|
|
|
guard pointCount > 1 else { return }
|
|
|
|
let spacing = width / CGFloat(pointCount - 1)
|
|
let points = data.enumerated().map { index, value in
|
|
let x = CGFloat(index) * spacing
|
|
let normalizedValue = min(max(value / maxValue, 0), 1)
|
|
let y = height * (1 - normalizedValue)
|
|
return CGPoint(x: x, y: y)
|
|
}
|
|
|
|
drawLine(context: context, points: points, height: height)
|
|
}
|
|
.frame(height: chartHeight)
|
|
}
|
|
|
|
private func drawGrid(context: GraphicsContext, width: CGFloat, height: CGFloat) {
|
|
let gridLineCount = 3
|
|
for i in 0 ... gridLineCount {
|
|
let y = height * CGFloat(i) / CGFloat(gridLineCount)
|
|
var path = Path()
|
|
path.move(to: CGPoint(x: 0, y: y))
|
|
path.addLine(to: CGPoint(x: width, y: y))
|
|
context.stroke(
|
|
path,
|
|
with: .color(gridColor),
|
|
style: StrokeStyle(lineWidth: 1, dash: [5, 5])
|
|
)
|
|
}
|
|
}
|
|
|
|
private func drawLine(context: GraphicsContext, points: [CGPoint], height: CGFloat) {
|
|
guard !points.isEmpty else { return }
|
|
|
|
var linePath = Path()
|
|
linePath.move(to: points[0])
|
|
for point in points.dropFirst() {
|
|
linePath.addLine(to: point)
|
|
}
|
|
|
|
context.stroke(
|
|
linePath,
|
|
with: .color(lineColor),
|
|
style: StrokeStyle(lineWidth: 2, lineCap: .round, lineJoin: .round)
|
|
)
|
|
|
|
var fillPath = linePath
|
|
if let lastPoint = points.last {
|
|
fillPath.addLine(to: CGPoint(x: lastPoint.x, y: height))
|
|
fillPath.addLine(to: CGPoint(x: 0, y: height))
|
|
fillPath.addLine(to: points[0])
|
|
}
|
|
|
|
context.fill(fillPath, with: .color(lineColor.opacity(0.1)))
|
|
}
|
|
}
|