@@ -112,44 +112,58 @@ public struct BarAudioVisualizer: View {
112112 GeometryReader { geometry in
113113 let highlightingSequence = animationProperties. highlightingSequence ( agentState: agentState)
114114 let highlighted = highlightingSequence [ animationPhase % highlightingSequence. count]
115- let duration = animationProperties. duration ( agentState: agentState)
116115
117116 bars ( geometry: geometry, highlighted: highlighted)
118117 . 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) )
126119 }
127120 . onDisappear {
128- animationTask ? . cancel ( )
121+ stopAnimation ( )
129122 }
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) )
133125 }
126+ . animation ( . easeInOut, value: animationPhase)
127+ . animation ( . easeInOut( duration: 0.3 ) , value: agentState)
134128 }
135129 }
136130
137131 @ViewBuilder
138132 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+
140138 HStack ( alignment: . center, spacing: geometry. size. width * barSpacingFactor) {
141139 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
150161 }
151162 }
152- . padding ( geometry. size. width * barSpacingFactor)
163+ }
164+
165+ private func stopAnimation( ) {
166+ animationTask? . cancel ( )
153167 }
154168}
155169
0 commit comments