mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-29 08:04:32 +00:00
Final Fixes: Flip Animation - wrap for the day
This commit is contained in:
parent
b6424a196f
commit
12f51615ae
7 changed files with 42 additions and 19 deletions
|
|
@ -544,6 +544,7 @@ struct ContentView: View {
|
||||||
.clipped()
|
.clipped()
|
||||||
.clipShape(RoundedRectangle(cornerRadius: MusicPlayerImageSizes.cornerRadiusInset.closed))
|
.clipShape(RoundedRectangle(cornerRadius: MusicPlayerImageSizes.cornerRadiusInset.closed))
|
||||||
.matchedGeometryEffect(id: "albumArt", in: albumArtNamespace)
|
.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 - 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)))
|
.frame(width: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12) + gestureProgress / 2), height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)))
|
||||||
|
|
|
||||||
|
|
@ -183,6 +183,7 @@ struct LockScreenMusicPanel: View {
|
||||||
.transition(.scale.combined(with: .opacity))
|
.transition(.scale.combined(with: .opacity))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.albumArtFlip(angle: musicManager.flipAngle)
|
||||||
.frame(width: size, height: size)
|
.frame(width: size, height: size)
|
||||||
.background(albumArtBackground(cornerRadius: cornerRadius))
|
.background(albumArtBackground(cornerRadius: cornerRadius))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ struct MinimalisticMusicView: View {
|
||||||
)
|
)
|
||||||
.clipped()
|
.clipped()
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 18)) // Dramatically increased corner radius for minimalistic mode
|
.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 - 12), height: max(0, vm.effectiveClosedNotchHeight - 12))
|
||||||
}
|
}
|
||||||
.frame(width: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)))
|
.frame(width: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)))
|
||||||
|
|
|
||||||
|
|
@ -268,11 +268,11 @@ struct MinimalisticAlbumArtView: View {
|
||||||
Image(nsImage: musicManager.albumArt)
|
Image(nsImage: musicManager.albumArt)
|
||||||
.resizable()
|
.resizable()
|
||||||
.aspectRatio(contentMode: .fill)
|
.aspectRatio(contentMode: .fill)
|
||||||
.animation(.spring(response: 0.4, dampingFraction: 0.8), value: musicManager.isFlipping)
|
|
||||||
)
|
)
|
||||||
.clipped()
|
.clipped()
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 12))
|
.clipShape(RoundedRectangle(cornerRadius: 12))
|
||||||
.matchedGeometryEffect(id: "albumArt", in: albumArtNamespace)
|
.matchedGeometryEffect(id: "albumArt", in: albumArtNamespace)
|
||||||
|
.albumArtFlip(angle: musicManager.flipAngle)
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
.opacity(musicManager.isPlaying ? 1 : 0.4)
|
.opacity(musicManager.isPlaying ? 1 : 0.4)
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ struct AlbumArtView: View {
|
||||||
albumArtImage
|
albumArtImage
|
||||||
appIconOverlay
|
appIconOverlay
|
||||||
}
|
}
|
||||||
|
.albumArtFlip(angle: musicManager.flipAngle)
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
.scaleEffect(musicManager.isPlaying ? 1 : 0.85)
|
.scaleEffect(musicManager.isPlaying ? 1 : 0.85)
|
||||||
|
|
|
||||||
1
DynamicIsland/extensions/AlbumArtFlipModifier.swift
Normal file
1
DynamicIsland/extensions/AlbumArtFlipModifier.swift
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
// Deprecated placeholder: albumArtFlip modifier now lives alongside MusicManager.
|
||||||
|
|
@ -55,8 +55,8 @@ class MusicManager: ObservableObject {
|
||||||
private var lastArtworkAlbum: String = "Self Love"
|
private var lastArtworkAlbum: String = "Self Love"
|
||||||
private var lastArtworkBundleIdentifier: String? = nil
|
private var lastArtworkBundleIdentifier: String? = nil
|
||||||
|
|
||||||
@Published var isFlipping: Bool = false
|
@Published var flipAngle: Double = 0
|
||||||
private var flipWorkItem: DispatchWorkItem?
|
private let flipAnimationDuration: TimeInterval = 0.45
|
||||||
|
|
||||||
@Published var isTransitioning: Bool = false
|
@Published var isTransitioning: Bool = false
|
||||||
private var transitionWorkItem: DispatchWorkItem?
|
private var transitionWorkItem: DispatchWorkItem?
|
||||||
|
|
@ -93,7 +93,6 @@ class MusicManager: ObservableObject {
|
||||||
debounceIdleTask?.cancel()
|
debounceIdleTask?.cancel()
|
||||||
cancellables.removeAll()
|
cancellables.removeAll()
|
||||||
controllerCancellables.removeAll()
|
controllerCancellables.removeAll()
|
||||||
flipWorkItem?.cancel()
|
|
||||||
transitionWorkItem?.cancel()
|
transitionWorkItem?.cancel()
|
||||||
|
|
||||||
// Release active controller
|
// Release active controller
|
||||||
|
|
@ -159,9 +158,6 @@ class MusicManager: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setActiveController(_ controller: any MediaControllerProtocol) {
|
private func setActiveController(_ controller: any MediaControllerProtocol) {
|
||||||
// Cancel any existing flip animation
|
|
||||||
flipWorkItem?.cancel()
|
|
||||||
|
|
||||||
// Set new active controller
|
// Set new active controller
|
||||||
activeController = controller
|
activeController = controller
|
||||||
|
|
||||||
|
|
@ -270,19 +266,9 @@ class MusicManager: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func triggerFlipAnimation() {
|
private func triggerFlipAnimation() {
|
||||||
// Cancel any existing animation
|
withAnimation(.easeInOut(duration: flipAnimationDuration)) {
|
||||||
flipWorkItem?.cancel()
|
flipAngle += 180
|
||||||
|
|
||||||
// Create a new animation
|
|
||||||
let workItem = DispatchWorkItem { [weak self] in
|
|
||||||
self?.isFlipping = true
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
|
||||||
self?.isFlipping = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
flipWorkItem = workItem
|
|
||||||
DispatchQueue.main.async(execute: workItem)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateArtwork(_ artworkData: Data) {
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue