Final Fixes: Flip Animation - wrap for the day

This commit is contained in:
Hariharan Mudaliar 2025-10-17 23:43:33 +05:30
commit 12f51615ae
7 changed files with 42 additions and 19 deletions

View file

@ -544,6 +544,7 @@ struct ContentView: View {
.clipped()
.clipShape(RoundedRectangle(cornerRadius: MusicPlayerImageSizes.cornerRadiusInset.closed))
.matchedGeometryEffect(id: "albumArt", in: albumArtNamespace)
.albumArtFlip(angle: musicManager.flipAngle)
.frame(width: max(0, vm.effectiveClosedNotchHeight - 12), height: max(0, vm.effectiveClosedNotchHeight - 12))
}
.frame(width: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12) + gestureProgress / 2), height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)))

View file

@ -183,6 +183,7 @@ struct LockScreenMusicPanel: View {
.transition(.scale.combined(with: .opacity))
}
}
.albumArtFlip(angle: musicManager.flipAngle)
.frame(width: size, height: size)
.background(albumArtBackground(cornerRadius: cornerRadius))
}

View file

@ -58,6 +58,7 @@ struct MinimalisticMusicView: View {
)
.clipped()
.clipShape(RoundedRectangle(cornerRadius: 18)) // Dramatically increased corner radius for minimalistic mode
.albumArtFlip(angle: musicManager.flipAngle)
.frame(width: max(0, vm.effectiveClosedNotchHeight - 12), height: max(0, vm.effectiveClosedNotchHeight - 12))
}
.frame(width: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)))

View file

@ -268,11 +268,11 @@ struct MinimalisticAlbumArtView: View {
Image(nsImage: musicManager.albumArt)
.resizable()
.aspectRatio(contentMode: .fill)
.animation(.spring(response: 0.4, dampingFraction: 0.8), value: musicManager.isFlipping)
)
.clipped()
.clipShape(RoundedRectangle(cornerRadius: 12))
.matchedGeometryEffect(id: "albumArt", in: albumArtNamespace)
.albumArtFlip(angle: musicManager.flipAngle)
}
.buttonStyle(PlainButtonStyle())
.opacity(musicManager.isPlaying ? 1 : 0.4)

View file

@ -65,6 +65,7 @@ struct AlbumArtView: View {
albumArtImage
appIconOverlay
}
.albumArtFlip(angle: musicManager.flipAngle)
}
.buttonStyle(PlainButtonStyle())
.scaleEffect(musicManager.isPlaying ? 1 : 0.85)

View file

@ -0,0 +1 @@
// Deprecated placeholder: albumArtFlip modifier now lives alongside MusicManager.

View file

@ -55,8 +55,8 @@ class MusicManager: ObservableObject {
private var lastArtworkAlbum: String = "Self Love"
private var lastArtworkBundleIdentifier: String? = nil
@Published var isFlipping: Bool = false
private var flipWorkItem: DispatchWorkItem?
@Published var flipAngle: Double = 0
private let flipAnimationDuration: TimeInterval = 0.45
@Published var isTransitioning: Bool = false
private var transitionWorkItem: DispatchWorkItem?
@ -93,7 +93,6 @@ class MusicManager: ObservableObject {
debounceIdleTask?.cancel()
cancellables.removeAll()
controllerCancellables.removeAll()
flipWorkItem?.cancel()
transitionWorkItem?.cancel()
// Release active controller
@ -159,9 +158,6 @@ class MusicManager: ObservableObject {
}
private func setActiveController(_ controller: any MediaControllerProtocol) {
// Cancel any existing flip animation
flipWorkItem?.cancel()
// Set new active controller
activeController = controller
@ -270,19 +266,9 @@ class MusicManager: ObservableObject {
}
private func triggerFlipAnimation() {
// Cancel any existing animation
flipWorkItem?.cancel()
// Create a new animation
let workItem = DispatchWorkItem { [weak self] in
self?.isFlipping = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self?.isFlipping = false
}
withAnimation(.easeInOut(duration: flipAnimationDuration)) {
flipAngle += 180
}
flipWorkItem = workItem
DispatchQueue.main.async(execute: workItem)
}
private func updateArtwork(_ artworkData: Data) {
@ -447,3 +433,35 @@ class MusicManager: ObservableObject {
}
}
}
// MARK: - Album Art Flip Helper
private struct AlbumArtFlipModifier: ViewModifier {
let angle: Double
private var normalizedAngle: Double {
var value = angle.truncatingRemainder(dividingBy: 360)
if value < 0 { value += 360 }
return value
}
private var mirrorScale: CGFloat {
(normalizedAngle > 90 && normalizedAngle < 270) ? -1 : 1
}
func body(content: Content) -> some View {
content
.rotation3DEffect(
.degrees(angle),
axis: (x: 0, y: 1, z: 0),
perspective: 0.65
)
.scaleEffect(x: mirrorScale, y: 1)
}
}
extension View {
func albumArtFlip(angle: Double) -> some View {
modifier(AlbumArtFlipModifier(angle: angle))
}
}