Skip to content

Instantly share code, notes, and snippets.

@pabliqe
Last active March 14, 2026 04:39
Show Gist options
  • Select an option

  • Save pabliqe/f803cbeedb949e5673c00592e06212a9 to your computer and use it in GitHub Desktop.

Select an option

Save pabliqe/f803cbeedb949e5673c00592e06212a9 to your computer and use it in GitHub Desktop.
import type { ComponentType } from "react"
import { useRef, useState, useEffect } from "react"
import {
motion,
useScroll,
useVelocity,
useTransform,
useSpring,
} from "framer-motion"
// --- CONFIGURATION ---
const CONFIG = {
randomIntensity: 0.5, // Higher = more chance | Range 0–1
intensity: 1.5, // Higher = more movement (multiplier)
maxRotation: 15, // Max degrees it can tilt
maxTranslate: 100, // Max pixels it can shift X/Y
// Inertia Settings (The "Physics")
stiffness: 300, // Higher = Snappier/Faster reaction | Range: 1–1000 (400)
damping: 50, // Higher = Stops sooner (less "jiggle") | Range: 0–100 (30)
mass: 1, // Higher = Feels "heavier" | Range: 0.1—10 (1)
}
function randomize() {
return Math.floor(Math.random() * (1 - 0 + 1))
}
export function withRotation(Component): ComponentType {
return (props) => {
// Generate a stable random seed for this instance (-0.5 to 0.5)
const [randomSeed] = useState(() => Math.random() - 0.5)
const { scrollY } = useScroll()
const scrollVelocity = useVelocity(scrollY)
// The "Spring" creates the inertia/wind feel
const smoothVelocity = useSpring(scrollVelocity, {
mass: CONFIG.mass,
stiffness: CONFIG.stiffness,
damping: CONFIG.damping,
})
// Velocity range: -3000 (fast up) to 3000 (fast down)
const velocityRange = [-3000, 3000]
// Calculate the "Impact"
// If randomIntensity is 0.5, this results in a multiplier between 0.75 and 1.25
const calcIntensity =
CONFIG.intensity * (1 + randomSeed * CONFIG.randomIntensity)
// Map velocity to movement based on CONFIG
const rotate = useTransform(smoothVelocity, velocityRange, [
CONFIG.maxRotation * calcIntensity,
-CONFIG.maxRotation * calcIntensity,
])
return (
<Component
{...props}
style={{
...props.style,
rotate,
}}
/>
)
}
}
export function withInertia(Component): ComponentType {
return (props) => {
// Generate a stable random seed for this instance (-0.5 to 0.5)
const [randomSeed] = useState(() => Math.random() - 0.5)
const { scrollY } = useScroll()
const scrollVelocity = useVelocity(scrollY)
// The "Spring" creates the inertia/wind feel
const smoothVelocity = useSpring(scrollVelocity, {
mass: CONFIG.mass,
stiffness: CONFIG.stiffness,
damping: CONFIG.damping,
})
// Velocity range: -3000 (fast up) to 3000 (fast down)
const velocityRange = [-3000, 3000]
// Map velocity to movement based on CONFIG
// const rotate = useTransform(smoothVelocity, velocityRange, [
// CONFIG.maxRotation * 0.5,
// -(CONFIG.maxRotation * 0.5),
// ])
// const translateX = useTransform(smoothVelocity, velocityRange, [
// -CONFIG.maxTranslate * CONFIG.intensity,
// CONFIG.maxTranslate * CONFIG.intensity,
// ])
// Calculate the "Impact"
// If randomIntensity is 0.5, this results in a multiplier between 0.75 and 1.25
const calcIntensity =
CONFIG.intensity * (1 + randomSeed * CONFIG.randomIntensity)
const translateY = useTransform(smoothVelocity, velocityRange, [
CONFIG.maxTranslate * calcIntensity,
-CONFIG.maxTranslate * calcIntensity,
])
return (
<Component
{...props}
style={{
...props.style,
// rotate,
// X: translateX,
y: translateY,
// Optional: add a slight Y lift when scrolling fast
// y: useTransform(
// smoothVelocity,
// [-3000, 0, 3000],
// [-10, 0, -10]
// ),
}}
/>
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment