From 49f4cf168f0759d2fa6f4a30ccfa7216c0640606 Mon Sep 17 00:00:00 2001 From: Trey t Date: Fri, 26 Dec 2025 21:35:42 -0600 Subject: [PATCH] Simplify task completion animations to 4 celebration types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep only implode, firework, starburst, and ripple animations. Remove slide, fade, scale, flip, bounce, spring, rotation, morph, confetti, and cascade animations for a more focused experience. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../AnimationTestingView.swift | 2 +- .../AnimationTesting/TaskAnimations.swift | 231 +----------------- 2 files changed, 8 insertions(+), 225 deletions(-) diff --git a/iosApp/iosApp/Profile/AnimationTesting/AnimationTestingView.swift b/iosApp/iosApp/Profile/AnimationTesting/AnimationTestingView.swift index 2891f37..4cd15e9 100644 --- a/iosApp/iosApp/Profile/AnimationTesting/AnimationTestingView.swift +++ b/iosApp/iosApp/Profile/AnimationTesting/AnimationTestingView.swift @@ -4,7 +4,7 @@ struct AnimationTestingView: View { @Environment(\.dismiss) private var dismiss // Animation selection - @State private var selectedAnimation: TaskAnimationType = .slide + @State private var selectedAnimation: TaskAnimationType = .implode // Fake task data @State private var columns: [TestColumn] = TestColumn.defaultColumns diff --git a/iosApp/iosApp/Profile/AnimationTesting/TaskAnimations.swift b/iosApp/iosApp/Profile/AnimationTesting/TaskAnimations.swift index 0e114e3..1ef4183 100644 --- a/iosApp/iosApp/Profile/AnimationTesting/TaskAnimations.swift +++ b/iosApp/iosApp/Profile/AnimationTesting/TaskAnimations.swift @@ -3,16 +3,6 @@ import SwiftUI // MARK: - Animation Type Enum enum TaskAnimationType: String, CaseIterable, Identifiable { - case slide = "Slide" - case fade = "Fade" - case scale = "Scale" - case flip = "Flip" - case bounce = "Bounce" - case spring = "Spring" - case rotation = "Rotation" - case morph = "Morph" - case confetti = "Confetti" - case cascade = "Cascade" case implode = "Implode" case firework = "Firework" case starburst = "Starburst" @@ -22,16 +12,6 @@ enum TaskAnimationType: String, CaseIterable, Identifiable { var icon: String { switch self { - case .slide: return "arrow.right" - case .fade: return "circle.lefthalf.filled" - case .scale: return "arrow.up.left.and.arrow.down.right" - case .flip: return "arrow.triangle.2.circlepath" - case .bounce: return "arrow.up.arrow.down" - case .spring: return "waveform.path" - case .rotation: return "rotate.right" - case .morph: return "circle.hexagongrid.fill" - case .confetti: return "sparkles" - case .cascade: return "stairs" case .implode: return "checkmark.circle" case .firework: return "sparkle" case .starburst: return "sun.max.fill" @@ -41,16 +21,6 @@ enum TaskAnimationType: String, CaseIterable, Identifiable { var description: String { switch self { - case .slide: return "Slides horizontally to next column" - case .fade: return "Fades out and in at destination" - case .scale: return "Shrinks, moves, then grows" - case .flip: return "3D flip rotation during move" - case .bounce: return "Bouncy overshoot animation" - case .spring: return "Physics-based spring motion" - case .rotation: return "Spins while moving" - case .morph: return "Shape morphs during transition" - case .confetti: return "Celebration particles on complete" - case .cascade: return "Multi-stage staggered animation" case .implode: return "Sucks into center, becomes checkmark" case .firework: return "Explodes into colorful sparks" case .starburst: return "Radiating rays from checkmark" @@ -58,15 +28,8 @@ enum TaskAnimationType: String, CaseIterable, Identifiable { } } - /// Whether this animation needs special timing (longer hold for checkmark display) - var needsExtendedTiming: Bool { - switch self { - case .implode, .firework, .starburst, .ripple: - return true - default: - return false - } - } + /// All celebration animations need extended timing for checkmark display + var needsExtendedTiming: Bool { true } } // MARK: - Animation Phase @@ -190,91 +153,12 @@ struct AnimationChip: View { } } -// MARK: - Confetti Particle System - -struct ConfettiParticle: Identifiable { - let id: Int - let color: Color - let size: CGFloat - var x: CGFloat - var y: CGFloat - var rotation: Double - var opacity: Double -} - -struct ConfettiView: View { - @State private var particles: [ConfettiParticle] = [] - - private let colors: [Color] = [ - .appPrimary, .appAccent, .appSecondary, .green, .orange, .pink, .purple - ] - - var body: some View { - ZStack { - ForEach(particles) { particle in - RoundedRectangle(cornerRadius: 2) - .fill(particle.color) - .frame(width: particle.size, height: particle.size * 0.6) - .rotationEffect(.degrees(particle.rotation)) - .offset(x: particle.x, y: particle.y) - .opacity(particle.opacity) - } - } - .onAppear { generateParticles() } - } - - private func generateParticles() { - particles = (0..<25).map { i in - ConfettiParticle( - id: i, - color: colors.randomElement()!, - size: CGFloat.random(in: 6...10), - x: CGFloat.random(in: -60...60), - y: CGFloat.random(in: -20...20), - rotation: Double.random(in: 0...360), - opacity: 1.0 - ) - } - - withAnimation(.easeOut(duration: 1.2)) { - particles = particles.map { p in - var particle = p - particle.y += CGFloat.random(in: 80...150) - particle.x += CGFloat.random(in: -30...30) - particle.rotation += Double.random(in: 180...540) - particle.opacity = 0 - return particle - } - } - } -} - // MARK: - Animation View Modifiers extension View { @ViewBuilder func taskAnimation(type: TaskAnimationType, phase: AnimationPhase) -> some View { switch type { - case .slide: - self.slideAnimation(phase: phase) - case .fade: - self.fadeAnimation(phase: phase) - case .scale: - self.scaleAnimation(phase: phase) - case .flip: - self.flipAnimation(phase: phase) - case .bounce: - self.bounceAnimation(phase: phase) - case .spring: - self.springAnimation(phase: phase) - case .rotation: - self.rotationAnimation(phase: phase) - case .morph: - self.morphAnimation(phase: phase) - case .confetti: - self.confettiAnimation(phase: phase) - case .cascade: - self.cascadeAnimation(phase: phase) case .implode: self.implodeAnimation(phase: phase) case .firework: @@ -287,112 +171,11 @@ extension View { } } -// MARK: - Individual Animation Implementations +// MARK: - Animation Implementations private extension View { - // 1. Slide - Horizontal slide with opacity - func slideAnimation(phase: AnimationPhase) -> some View { - self - .offset(x: phase == .exiting ? 80 : (phase == .entering ? -80 : 0)) - .opacity(phase == .exiting || phase == .entering ? 0.3 : 1.0) - .animation(.easeInOut(duration: 0.4), value: phase) - } - - // 2. Fade - Simple fade out/in - func fadeAnimation(phase: AnimationPhase) -> some View { - self - .opacity(phase == .idle || phase == .complete ? 1.0 : 0.0) - .animation(.easeInOut(duration: 0.35), value: phase) - } - - // 3. Scale - Shrink to point and expand - func scaleAnimation(phase: AnimationPhase) -> some View { - self - .scaleEffect(phase == .moving ? 0.1 : 1.0) - .opacity(phase == .moving ? 0.3 : 1.0) - .animation(.easeInOut(duration: 0.4), value: phase) - } - - // 4. Flip - 3D horizontal flip - func flipAnimation(phase: AnimationPhase) -> some View { - self - .rotation3DEffect( - .degrees(phase == .exiting ? 90 : (phase == .entering ? -90 : 0)), - axis: (x: 0, y: 1, z: 0), - perspective: 0.5 - ) - .opacity(phase == .moving ? 0 : 1.0) - .animation(.easeInOut(duration: 0.45), value: phase) - } - - // 5. Bounce - Overshoot with vertical movement - func bounceAnimation(phase: AnimationPhase) -> some View { - self - .offset(y: phase == .exiting ? -25 : (phase == .entering ? 15 : 0)) - .scaleEffect(phase == .entering ? 1.15 : (phase == .exiting ? 0.9 : 1.0)) - .animation(.interpolatingSpring(stiffness: 120, damping: 8), value: phase) - } - - // 6. Spring - Physics-based spring - func springAnimation(phase: AnimationPhase) -> some View { - self - .offset(x: phase == .exiting ? 40 : 0, y: phase == .moving ? -20 : 0) - .scaleEffect(phase == .entering ? 0.92 : 1.0) - .animation(.spring(response: 0.55, dampingFraction: 0.6, blendDuration: 0), value: phase) - } - - // 7. Rotation - 360 degree spin - func rotationAnimation(phase: AnimationPhase) -> some View { - self - .rotationEffect(.degrees(phase == .moving || phase == .exiting ? 360 : 0)) - .scaleEffect(phase == .moving ? 0.75 : 1.0) - .animation(.easeInOut(duration: 0.5), value: phase) - } - - // 8. Morph - Shape change during transition - func morphAnimation(phase: AnimationPhase) -> some View { - self - .clipShape(RoundedRectangle( - cornerRadius: phase == .moving ? 60 : AppRadius.lg, - style: .continuous - )) - .scaleEffect( - x: phase == .moving ? 0.6 : 1.0, - y: phase == .moving ? 1.3 : 1.0 - ) - .opacity(phase == .moving ? 0.7 : 1.0) - .animation(.easeInOut(duration: 0.45), value: phase) - } - - // 9. Confetti - Celebration burst - func confettiAnimation(phase: AnimationPhase) -> some View { - self - .overlay { - if phase == .complete { - ConfettiView() - .allowsHitTesting(false) - } - } - .scaleEffect(phase == .exiting ? 1.1 : (phase == .complete ? 1.05 : 1.0)) - .animation(.spring(response: 0.35, dampingFraction: 0.5), value: phase) - } - - // 10. Cascade - Multi-stage staggered animation - func cascadeAnimation(phase: AnimationPhase) -> some View { - self - .scaleEffect(phase == .exiting ? 0.85 : 1.0) - .rotationEffect(.degrees(phase == .exiting ? -8 : (phase == .entering ? 8 : 0))) - .offset(y: phase == .moving ? -35 : 0) - .opacity(phase == .moving ? 0.6 : 1.0) - .animation( - .easeInOut(duration: 0.25) - .delay(phase == .entering ? 0.15 : 0), - value: phase - ) - } - - // 11. Implode - Sucks into center, becomes checkmark, disappears + // Implode - Sucks into center, becomes checkmark, disappears func implodeAnimation(phase: AnimationPhase) -> some View { ZStack { // Card content - shrinks and hides @@ -409,7 +192,7 @@ private extension View { } } - // 12. Firework - Explodes into colorful sparks with checkmark + // Firework - Explodes into colorful sparks with checkmark func fireworkAnimation(phase: AnimationPhase) -> some View { ZStack { // Card content - shrinks and hides @@ -426,7 +209,7 @@ private extension View { } } - // 13. Starburst - Radiating rays from checkmark + // Starburst - Radiating rays from checkmark func starburstAnimation(phase: AnimationPhase) -> some View { ZStack { // Card content - shrinks and hides @@ -443,7 +226,7 @@ private extension View { } } - // 14. Ripple - Checkmark with expanding rings + // Ripple - Checkmark with expanding rings func rippleAnimation(phase: AnimationPhase) -> some View { ZStack { // Card content - shrinks and hides