Physics & Gestures6 min read
Elastic Physics Pull-to-Refresh Gesture
Direct Definition & Architecture
An elastic pull-to-refresh interaction pairs continuous pointer drag gestures with logarithmic friction. As the user pulls downward, resistance increases non-linearly until reaching a trigger threshold, transitioning the state machine from Pulling to Refreshing.
Live Physics Canvas
120 FPS GPU RenderInteractive elastic pull simulation
Stiffness (Tension)300
Damping (Friction)24
Technical Specifications
Gesture Pipeline
Touch & Pointer gesture responder
Resistance Curve
Logarithmic friction clamp
State Hierarchy
Idle → Pulling → Triggered → Fetching → Reset
Mobile Native
Zero scroll-jacking conflict
State Machine Graph
IdleContent resting at scroll top 0.
PullingDisplacement with logarithmic friction applied.
Threshold ReachedTrigger fired; spinner arms.
RefreshingIndicator pinned at 64px while asynchronous promise resolves.
RetractingSpring snaps content container back to 0.
Production Code Output
pull-to-refresh.jsx
import React, { useState } from 'react';
import { motion, useMotionValue, useTransform } from 'framer-motion';
export function PullToRefresh({ onRefresh, children }) {
const [status, setStatus] = useState('idle'); // idle | pulling | refreshing
const y = useMotionValue(0);
const opacity = useTransform(y, [0, 80], [0, 1]);
const rotate = useTransform(y, [0, 100], [0, 360]);
const handleDragEnd = async () => {
if (y.get() >= 80) {
setStatus('refreshing');
await onRefresh?.();
setStatus('idle');
}
};
return (
<div className="relative overflow-hidden w-full max-w-md mx-auto rounded-2xl bg-stone-900 border border-stone-800">
<motion.div
style={{ opacity, rotate }}
className="absolute top-4 left-1/2 -translate-x-1/2 z-10 w-8 h-8 rounded-full border-2 border-violet-500 border-t-transparent animate-spin"
/>
<motion.div
drag="y"
dragConstraints={{ top: 0, bottom: 120 }}
dragElastic={0.4}
style={{ y }}
onDragEnd={handleDragEnd}
className="p-6 bg-stone-950 min-h-[300px]"
>
{children}
</motion.div>
</div>
);
}Architectural Breakdown
Bad pull-to-refresh implementations lock the scroll position or feel linear and gummy.
ExodeUI applies physical resistance formulas where drag distance yields diminishing vertical displacement.
Once triggered, the indicator rests at a comfortable 64px offset during data loading, then snaps smoothly back once the network promise resolves.
Frequently Asked Questions
Explore Related Motion Guides & Tooling
Build your next living interface today.
Design fluid physics and visual logic in ExodeUI and export directly to React, Swift, and Flutter.