Interactive Gesture Animation Implementation in Mobile Apps
The difference between gesture animation and regular animation is interruptibility. User drags a card, changes mind midway, and it should return with physically sound behavior—not just jump to start, but "spring back" accounting for accumulated speed. This is interactive gesture animation—animation controlled by finger in real time.
Physics—Foundation of Right Feeling
The key parameter is the connection between gesture release velocity (gestureRecognizer.velocity) and finish animation parameters. If they're not synced, you get a "jump": finger moves at one speed, object finishes at another.
On iOS, UIViewPropertyAnimator solves this natively via continueAnimation(withTimingParameters:durationFactor:):
@objc func handlePan(_ gesture: UIPanGestureRecognizer) {
let translation = gesture.translation(in: view)
switch gesture.state {
case .changed:
animator?.fractionComplete = translation.y / totalDistance
case .ended:
let velocity = gesture.velocity(in: view)
let springVelocity = abs(velocity.y) / (totalDistance - translation.y)
let timing = UISpringTimingParameters(dampingRatio: 0.8,
initialVelocity: CGVector(dx: 0, dy: springVelocity))
animator?.continueAnimation(withTimingParameters: timing, durationFactor: 0)
default: break
}
}
fractionComplete is direct animation progress control. Value 0.0 is start, 1.0 is end. User literally "drags" animation themselves.
Card Dismissal Swipe
Classic pattern in Tinder or to-do list style: card follows finger, on threshold—flies away, below threshold—returns.
Three parameters to calibrate:
- Release threshold—usually 30-40% screen width or velocity > 800 pt/s
-
Rotation angle—
transform = CGAffineTransform(rotationAngle: translation.x / screenWidth * 0.3)creates physics feel - Fly velocity—must match gesture speed; otherwise card "lags" mid-flight
On Android, implement via ViewDragHelper or ItemTouchHelper (for RecyclerView). ItemTouchHelper is good for lists but limited: custom feedback drawing requires onChildDraw override.
In Flutter, use Dismissible for simple cases, GestureDetector + AnimationController.fling() for complex. fling() accepts velocity directly from DragEndDetails.velocity.pixelsPerSecond—this is exactly the sync we're after.
Pull-to-Refresh with Custom Animation
Standard UIRefreshControl and SwipeRefreshLayout don't support custom animation. Implement via ScrollView with negative offset: when contentOffset.y < -threshold, show custom indicator with progress = abs(offset.y) / threshold. Lottie animation bound to setProgress() enables arbitrary design.
Drag & Drop with Physical Behavior
On iOS—UIDragInteraction + UIDropInteraction for cross-app DnD, UILongPressGestureRecognizer + UIViewPropertyAnimator for in-screen. Important: raising element gets scale animation (1.05) and shadow—visual signal "I'm holding object". On return—UISpringTimingParameters with high dampingRatio (0.9) for "soft landing" effect.
Testing and Calibration
Gesture animations must be tested on physical device—emulator doesn't reproduce exact touch physics. Slow Animations in simulator helps catch artifacts, but real feel is device-only. For parameter calibration, use Xcode Instruments → Animation Hitches to ensure GPU frame time doesn't exceed 16ms on 60Hz devices.
Timeline: 2–3 days per gesture animation pattern, including integration and calibration for specific design.







