@@ -112,44 +112,58 @@ public struct BarAudioVisualizer: View {
112
112
GeometryReader { geometry in
113
113
let highlightingSequence = animationProperties. highlightingSequence ( agentState: agentState)
114
114
let highlighted = highlightingSequence [ animationPhase % highlightingSequence. count]
115
- let duration = animationProperties. duration ( agentState: agentState)
116
115
117
116
bars ( geometry: geometry, highlighted: highlighted)
118
117
. onAppear {
119
- animationTask? . cancel ( )
120
- animationTask = Task {
121
- while !Task. isCancelled {
122
- try ? await Task . sleep ( nanoseconds: UInt64 ( duration * Double( NSEC_PER_SEC) ) )
123
- withAnimation ( . easeInOut) { animationPhase += 1 }
124
- }
125
- }
118
+ startAnimation ( duration: animationProperties. duration ( agentState: agentState) )
126
119
}
127
120
. onDisappear {
128
- animationTask ? . cancel ( )
121
+ stopAnimation ( )
129
122
}
130
- . animation ( . easeOut, value: agentState)
131
- . onChange ( of: agentState) { _ in
132
- animationPhase = 0
123
+ . onChange ( of: agentState) { newState in
124
+ startAnimation ( duration: animationProperties. duration ( agentState: newState) )
133
125
}
126
+ . animation ( . easeInOut, value: animationPhase)
127
+ . animation ( . easeInOut( duration: 0.3 ) , value: agentState)
134
128
}
135
129
}
136
130
137
131
@ViewBuilder
138
132
private func bars( geometry: GeometryProxy , highlighted: PhaseAnimationProperties . HighlightedBars ) -> some View {
139
- let barMinHeight = ( geometry. size. width - geometry. size. width * barSpacingFactor * CGFloat( barCount + 2 ) ) / CGFloat( barCount)
133
+ let totalSpacing = geometry. size. width * barSpacingFactor * CGFloat( barCount + 1 )
134
+ let availableWidth = geometry. size. width - totalSpacing
135
+ let barWidth = availableWidth / CGFloat( barCount)
136
+ let barMinHeight = barWidth // Use bar width as minimum height for square proportions
137
+
140
138
HStack ( alignment: . center, spacing: geometry. size. width * barSpacingFactor) {
141
139
ForEach ( 0 ..< audioProcessor. bands. count, id: \. self) { index in
142
- VStack {
143
- Spacer ( )
144
- RoundedRectangle ( cornerRadius: barMinHeight)
145
- . fill ( barColor)
146
- . opacity ( highlighted. contains ( index) ? 1 : barMinOpacity)
147
- . frame ( height: ( geometry. size. height - barMinHeight) * CGFloat( audioProcessor. bands [ index] ) + barMinHeight)
148
- Spacer ( )
149
- }
140
+ RoundedRectangle ( cornerRadius: barMinHeight)
141
+ . fill ( barColor)
142
+ . opacity ( highlighted. contains ( index) ? 1 : barMinOpacity)
143
+ . frame (
144
+ width: barWidth,
145
+ height: ( geometry. size. height - barMinHeight) * CGFloat( audioProcessor. bands [ index] ) + barMinHeight,
146
+ alignment: . center
147
+ )
148
+ . frame ( maxHeight: . infinity, alignment: . center)
149
+ }
150
+ }
151
+ . frame ( width: geometry. size. width)
152
+ }
153
+
154
+ private func startAnimation( duration: TimeInterval ) {
155
+ animationTask? . cancel ( )
156
+ animationPhase = 0
157
+ animationTask = Task {
158
+ while !Task. isCancelled {
159
+ try ? await Task . sleep ( nanoseconds: UInt64 ( duration * Double( NSEC_PER_SEC) ) )
160
+ animationPhase += 1
150
161
}
151
162
}
152
- . padding ( geometry. size. width * barSpacingFactor)
163
+ }
164
+
165
+ private func stopAnimation( ) {
166
+ animationTask? . cancel ( )
153
167
}
154
168
}
155
169
0 commit comments