ExodeUI

EXODEUI DESKTOP

A little space for good ideas.

Choose your platform to get the official ExodeUI installer.

Looking for Intel Mac or other builds? Explore the web studio or check all download options.

Prefer the browser? Open web studio →
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 Render

Interactive 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.

Try Exode for Free