Ver código fonte

Add professional hover lift animation to home action tiles.

This introduces a reusable hoverable tile view and animates icon elevation, shadow depth, and label emphasis so New meeting, Join, and Schedule feel more polished on pointer hover.

Made-with: Cursor
huzaifahayat12 4 dias atrás
pai
commit
eeb856b955
1 arquivos alterados com 43 adições e 2 exclusões
  1. 43 2
      zoom_app/ViewController.swift

+ 43 - 2
zoom_app/ViewController.swift

@@ -125,6 +125,34 @@ class ViewController: NSViewController {
125 125
         }
126 126
     }
127 127
 
128
+    private final class HoverTileView: NSView {
129
+        var onHoverChanged: ((Bool) -> Void)?
130
+        private var tracking: NSTrackingArea?
131
+
132
+        override func updateTrackingAreas() {
133
+            super.updateTrackingAreas()
134
+            if let tracking { removeTrackingArea(tracking) }
135
+            let area = NSTrackingArea(
136
+                rect: bounds,
137
+                options: [.activeInActiveApp, .mouseEnteredAndExited, .inVisibleRect],
138
+                owner: self,
139
+                userInfo: nil
140
+            )
141
+            addTrackingArea(area)
142
+            tracking = area
143
+        }
144
+
145
+        override func mouseEntered(with event: NSEvent) {
146
+            super.mouseEntered(with: event)
147
+            onHoverChanged?(true)
148
+        }
149
+
150
+        override func mouseExited(with event: NSEvent) {
151
+            super.mouseExited(with: event)
152
+            onHoverChanged?(false)
153
+        }
154
+    }
155
+
128 156
     private var palette = Palette(isDarkMode: true)
129 157
     private var darkModeEnabled: Bool {
130 158
         get {
@@ -3752,7 +3780,7 @@ class ViewController: NSViewController {
3752 3780
     }
3753 3781
 
3754 3782
     private func makeActionTile(title: String, symbol: String, color: NSColor, action: Selector? = nil) -> NSView {
3755
-        let root = NSView()
3783
+        let root = HoverTileView()
3756 3784
         root.translatesAutoresizingMaskIntoConstraints = false
3757 3785
         root.widthAnchor.constraint(equalToConstant: 88).isActive = true
3758 3786
         // Give caption text a little more vertical room so descenders (e.g. "g") are never clipped.
@@ -3779,8 +3807,9 @@ class ViewController: NSViewController {
3779 3807
             $0.translatesAutoresizingMaskIntoConstraints = false
3780 3808
             root.addSubview($0)
3781 3809
         }
3810
+        let iconTopConstraint = iconButton.topAnchor.constraint(equalTo: root.topAnchor)
3782 3811
         NSLayoutConstraint.activate([
3783
-            iconButton.topAnchor.constraint(equalTo: root.topAnchor),
3812
+            iconTopConstraint,
3784 3813
             iconButton.centerXAnchor.constraint(equalTo: root.centerXAnchor),
3785 3814
             iconButton.widthAnchor.constraint(equalToConstant: 50),
3786 3815
             iconButton.heightAnchor.constraint(equalToConstant: 50),
@@ -3788,6 +3817,18 @@ class ViewController: NSViewController {
3788 3817
             label.centerXAnchor.constraint(equalTo: root.centerXAnchor),
3789 3818
             label.bottomAnchor.constraint(equalTo: root.bottomAnchor)
3790 3819
         ])
3820
+        root.onHoverChanged = { [weak iconButton, weak label] hovering in
3821
+            NSAnimationContext.runAnimationGroup { context in
3822
+                context.duration = hovering ? 0.16 : 0.14
3823
+                context.timingFunction = CAMediaTimingFunction(name: hovering ? .easeOut : .easeInEaseOut)
3824
+                iconTopConstraint.animator().constant = hovering ? -3 : 0
3825
+                label?.animator().alphaValue = hovering ? 1.0 : 0.96
3826
+            }
3827
+            guard let iconButton else { return }
3828
+            iconButton.layer?.shadowOpacity = hovering ? 0.28 : 0.2
3829
+            iconButton.layer?.shadowRadius = hovering ? 10 : 7
3830
+            iconButton.layer?.shadowOffset = hovering ? NSSize(width: 0, height: -3) : NSSize(width: 0, height: -1)
3831
+        }
3791 3832
         return root
3792 3833
     }
3793 3834