CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-motion

A spring that solves your animation problems.

Pending
Overview
Eval results
Files

spring-system.mddocs/

Spring System

React Motion's spring system provides physics-based animation configurations that feel more natural than traditional easing curves. Instead of specifying durations, you define spring characteristics like stiffness and damping to control animation behavior.

Capabilities

spring Function

Creates a spring configuration object that defines how a value should animate to its target.

/**
 * Creates spring configuration for animated values
 * @param val - Target value for the animation
 * @param config - Optional spring configuration parameters
 * @returns OpaqueConfig object for internal use by Motion components
 */
function spring(val/*: number */, config/*?: SpringHelperConfig */)/*: OpaqueConfig */;

Usage Examples:

import { spring } from 'react-motion';

// Basic spring with default settings
const basicSpring = spring(100);

// Custom spring configuration
const customSpring = spring(100, {
  stiffness: 120,
  damping: 17,
  precision: 0.1
});

// Using in Motion component
<Motion
  style={{
    x: spring(targetX),
    y: spring(targetY, {stiffness: 200, damping: 20}),
    opacity: spring(1, {stiffness: 300, damping: 30})
  }}
>
  {({x, y, opacity}) => (
    <div style={{
      transform: `translate(${x}px, ${y}px)`,
      opacity
    }}>
      Animated element
    </div>
  )}
</Motion>

SpringHelperConfig Interface

Configuration object for customizing spring behavior.

/**
 * Configuration options for spring behavior
 * All properties are optional and have sensible defaults
 */
/*::
type SpringHelperConfig = {
  stiffness?: number, // Spring stiffness - higher values create snappier animations (default: 170)
  damping?: number,   // Spring damping - higher values reduce oscillation (default: 26)
  precision?: number, // Animation precision threshold - smaller values run longer (default: 0.01)
};
*/

OpaqueConfig Interface

Internal spring configuration object returned by the spring function. Not meant for direct manipulation.

/**
 * Internal spring configuration object
 * Used internally by Motion components - do not modify directly
 */
/*::
type OpaqueConfig = {
  val: number,      // Target value for animation
  stiffness: number, // Spring stiffness parameter
  damping: number,   // Spring damping parameter
  precision: number, // Precision threshold for stopping animation
};
*/

presets Object

Pre-defined spring configurations for common animation styles.

/**
 * Pre-defined spring configuration presets
 * Use these for common animation styles
 */
const presets = {
  /** Default balanced preset - smooth with minimal bounce */
  noWobble: { stiffness: 170, damping: 26 },
  /** Gentle, slower animation with soft easing */
  gentle: { stiffness: 120, damping: 14 },
  /** More oscillation and bounce */
  wobbly: { stiffness: 180, damping: 12 },
  /** Quick, snappy animation with minimal overshoot */
  stiff: { stiffness: 210, damping: 20 }
};

Usage Examples:

import { spring, presets } from 'react-motion';

// Using presets
const gentleSpring = spring(100, presets.gentle);
const wobblySpring = spring(50, presets.wobbly);
const stiffSpring = spring(200, presets.stiff);

// Comparing presets in action
function PresetDemo() {
  const [active, setActive] = useState(null);
  
  const presetConfigs = [
    { name: 'noWobble', config: presets.noWobble },
    { name: 'gentle', config: presets.gentle },
    { name: 'wobbly', config: presets.wobbly },
    { name: 'stiff', config: presets.stiff }
  ];
  
  return (
    <div>
      {presetConfigs.map(({ name, config }) => (
        <Motion
          key={name}
          style={{
            x: spring(active === name ? 200 : 0, config)
          }}
        >
          {({ x }) => (
            <div
              style={{
                transform: `translateX(${x}px)`,
                padding: '10px',
                margin: '5px 0',
                background: '#007bff',
                color: 'white',
                cursor: 'pointer',
                width: '200px'
              }}
              onClick={() => setActive(active === name ? null : name)}
            >
              {name}: stiffness={config.stiffness}, damping={config.damping}
            </div>
          )}
        </Motion>
      ))}
    </div>
  );
}

stripStyle Function

Utility function that extracts plain numeric values from style objects containing spring configurations.

/**
 * Extracts plain numeric values from spring-configured style objects
 * @param style - Style object that may contain spring configurations
 * @returns PlainStyle object with numeric values only
 */
function stripStyle(style/*: Style */)/*: PlainStyle */;

Usage Examples:

import { stripStyle, spring } from 'react-motion';

// Style with springs
const styleWithSprings = {
  x: spring(100, { stiffness: 120 }),
  y: spring(200),
  opacity: 1, // plain number
  scale: spring(1.5, { damping: 20 })
};

// Extract plain values
const plainStyle = stripStyle(styleWithSprings);
// Result: { x: 100, y: 200, opacity: 1, scale: 1.5 }

// Useful for getting initial values
function MyComponent() {
  const targetStyle = {
    width: spring(expanded ? 300 : 100),
    height: spring(expanded ? 200 : 50)
  };
  
  return (
    <Motion
      defaultStyle={stripStyle(targetStyle)} // Initial values without animation
      style={targetStyle}
    >
      {interpolatedStyle => (
        <div style={interpolatedStyle}>
          Content
        </div>
      )}
    </Motion>
  );
}

Spring Physics Explained

Stiffness Parameter

Controls how quickly the spring tries to reach its target:

  • Low (60-120): Slower, more gentle animations
  • Medium (120-200): Balanced, natural feeling
  • High (200-300): Quick, snappy animations
// Slow, gentle movement
spring(100, { stiffness: 80 })

// Quick, responsive movement  
spring(100, { stiffness: 250 })

Damping Parameter

Controls how much the spring oscillates around its target:

  • Low (8-15): More bounce and oscillation
  • Medium (15-30): Balanced with slight overshoot
  • High (30-50): Heavily damped, minimal overshoot
// Bouncy animation
spring(100, { damping: 10 })

// Smooth with no overshoot
spring(100, { damping: 40 })

Precision Parameter

Controls when the animation stops:

  • Smaller values (0.001-0.01): Animation runs longer, smoother ending
  • Larger values (0.1-1.0): Animation stops sooner, may feel abrupt
// Very precise, runs until nearly perfect
spring(100, { precision: 0.001 })

// Less precise, stops when "close enough"
spring(100, { precision: 0.1 })

Choosing Spring Parameters

Common Combinations

// UI Elements (buttons, menus)
spring(value, { stiffness: 300, damping: 30 })

// Layout changes (expanding panels)
spring(value, { stiffness: 120, damping: 17 })

// Playful interactions (hover effects)
spring(value, { stiffness: 180, damping: 12 })

// Smooth, professional (form transitions)
spring(value, { stiffness: 170, damping: 26 })

// Snappy mobile interactions
spring(value, { stiffness: 400, damping: 28 })

Guidelines by Use Case

Modal/Dialog Animations:

// Entry
{ stiffness: 300, damping: 30 }
// Exit  
{ stiffness: 400, damping: 40 }

List Item Transitions:

{ stiffness: 200, damping: 22 }

Drag and Drop:

// While dragging
{ stiffness: 400, damping: 40 }
// Snap back
{ stiffness: 300, damping: 30 }

Loading Animations:

{ stiffness: 150, damping: 15 } // Bouncy for attention

Advanced Usage

Conditional Spring Configs

function AdaptiveSpring({ urgent, value }) {
  const config = urgent 
    ? { stiffness: 400, damping: 28 } // Fast for urgent changes
    : { stiffness: 120, damping: 20 }; // Gentle for normal changes
    
  return (
    <Motion style={{ x: spring(value, config) }}>
      {({ x }) => <div style={{ transform: `translateX(${x}px)` }} />}
    </Motion>
  );
}

Dynamic Spring Parameters

function DynamicSpring() {
  const [distance, setDistance] = useState(0);
  
  // Adjust stiffness based on distance
  const stiffness = Math.max(120, Math.min(300, distance * 2));
  
  return (
    <Motion
      style={{
        x: spring(distance, { stiffness, damping: 20 })
      }}
    >
      {({ x }) => (
        <div
          style={{ transform: `translateX(${x}px)` }}
          onClick={(e) => setDistance(e.clientX)}
        >
          Click to move (stiffness: {stiffness})
        </div>
      )}
    </Motion>
  );
}

Mixed Style Objects

// Combining springs and static values
const mixedStyle = {
  // Animated properties
  x: spring(targetX, presets.stiff),
  y: spring(targetY, presets.gentle),
  scale: spring(targetScale),
  
  // Static properties (no animation)
  color: 'blue',
  fontSize: 16,
  fontWeight: 'bold'
};

Performance Considerations

Spring Calculation Cost

  • More complex springs (lower precision) require more calculations
  • High stiffness values may cause more frequent updates
  • Consider using presets for optimal performance

Animation Duration

Unlike CSS transitions, spring animations don't have fixed durations:

  • Stiff springs with high damping finish quickly
  • Soft springs with low damping take longer
  • Very low precision values can cause unnecessarily long animations

Memory Usage

  • Each spring maintains internal state (position, velocity)
  • Multiple springs on the same element share animation frames
  • Springs automatically clean up when components unmount

Install with Tessl CLI

npx tessl i tessl/npm-react-motion

docs

index.md

motion.md

spring-system.md

staggered-motion.md

transition-motion.md

tile.json