| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960 |
- import SwiftUI
- /// Canvas-based waveform visualization with playback progress.
- struct WaveformView: View {
- @Environment(PlayerViewModel.self) private var playerVM
- @EnvironmentObject private var theme: AppTheme
- @State private var isDragging = false
- @State private var dragProgress: Double = 0
- var body: some View {
- GeometryReader { geo in
- Canvas { context, size in
- let samples = playerVM.waveformSamples
- guard !samples.isEmpty else { return }
- let barWidth = size.width / CGFloat(samples.count)
- let midY = size.height / 2
- let progress = isDragging ? dragProgress : playerVM.progress
- for (index, sample) in samples.enumerated() {
- let x = CGFloat(index) * barWidth
- let isPlayed = Double(index) / Double(samples.count) < progress
- let topHeight = CGFloat(sample.max) * midY
- let bottomHeight = CGFloat(-sample.min) * midY
- let rect = CGRect(
- x: x,
- y: midY - topHeight,
- width: max(barWidth - 0.5, 0.5),
- height: topHeight + bottomHeight
- )
- let color = isPlayed ? theme.seekbarForeground : theme.seekbarBackground
- context.fill(Path(rect), with: .color(color))
- }
- // Playhead line
- let playheadX = progress * Double(size.width)
- let playheadRect = CGRect(x: playheadX - 0.5, y: 0, width: 1, height: size.height)
- context.fill(Path(playheadRect), with: .color(theme.accent))
- }
- .gesture(
- DragGesture(minimumDistance: 0)
- .onChanged { value in
- isDragging = true
- dragProgress = max(0, min(1, Double(value.location.x / geo.size.width)))
- }
- .onEnded { value in
- let prog = max(0, min(1, Double(value.location.x / geo.size.width)))
- playerVM.seekToProgress(prog)
- isDragging = false
- }
- )
- }
- .clipShape(RoundedRectangle(cornerRadius: 6))
- .contentShape(Rectangle())
- }
- }
|