Zero-dependency physics-based spring animation library with gesture support
npm install @oxog/springkitcreateSpringValue()
createSpringGroup()
whileHover, whileTap, whileFocus, whileInView, whileDrag
createPathAnimation() for line drawing effects
createMorph() for shape-to-shape transitions
getPathLength(), preparePathForAnimation(), getPointAtProgress()
flip(), flipBatch(), measureElement()
simulateSpring() to preview animation over time
calculatePeriod(), calculateDampingRatio()
isUnderdamped(), isCriticallyDamped(), isOverdamped()
globalLoop for FPS monitoring and animation tracking
AnimationState enum (Idle, Running, Paused, Complete)
clamp(), lerp(), mapRange(), degToRad(), radToDeg()
parseColor(), rgbToHex(), hexToRgb(), hslToRgb(), rgbToHsl()
useSpring, useSpringValue, useSprings, useTrail, useDrag, useGesture
useMotionValue, useTransform, useInView, useScroll, useAnimate
useVariants, VariantProvider
useReducedMotion for motion-sensitive users
, , , ,
for unmounting component animations
bash
npm install @oxog/springkit
`
Quick Start
`typescript
import { spring, springPresets } from '@oxog/springkit'
const anim = spring(0, 100, {
...springPresets.bounce,
onUpdate: (value) => {
element.style.transform = translateX(${value}px)
},
})
anim.start()
`
React
$3
`tsx
import { useSpring, Animated } from '@oxog/springkit/react'
function Box() {
const [isOpen, setIsOpen] = useState(false)
const style = useSpring({
scale: isOpen ? 1.2 : 1,
opacity: isOpen ? 1 : 0.5,
})
return (
onClick={() => setIsOpen(!isOpen)}
style={{ transform: scale(${style.scale}), opacity: style.opacity }}
/>
)
}
`
$3
`tsx
import { Animated } from '@oxog/springkit/react'
function InteractiveButton() {
return (
whileHover={{ scale: 1.05, backgroundColor: '#3b82f6' }}
whileTap={{ scale: 0.95 }}
whileFocus={{ boxShadow: '0 0 0 3px rgba(59, 130, 246, 0.5)' }}
>
Click Me
)
}
`
$3
`tsx
import { AnimatePresence, Animated } from '@oxog/springkit/react'
function Modal({ isOpen, onClose }) {
return (
{isOpen && (
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
onClick={onClose}
>
Modal Content
)}
)
}
`
$3
`tsx
import { createPathAnimation } from '@oxog/springkit'
const pathAnim = createPathAnimation(pathElement, {
config: { stiffness: 100, damping: 15 },
})
// Draw the path
await pathAnim.play()
// Reverse (erase)
await pathAnim.reverse()
`
$3
`typescript
import { keyframes } from '@oxog/springkit'
const anim = keyframes([0, 100, 50, 100], {
config: { stiffness: 200, damping: 20 },
onUpdate: (value) => {
element.style.opacity = String(value / 100)
},
})
await anim.play()
`
$3
`typescript
import { flip } from '@oxog/springkit'
// Animate layout change
await flip(element, () => {
element.classList.toggle('expanded')
}, {
config: { stiffness: 300, damping: 25 }
})
`
$3
`typescript
import { physicsPresets, getPhysicsPreset, createFeeling } from '@oxog/springkit'
// Use semantic presets directly
const anim = spring(0, 100, {
...physicsPresets.button, // Quick, responsive
onUpdate: (v) => element.style.transform = scale(${1 + v * 0.1})
})
// Or use "feelings" for quick configuration
const config = createFeeling('bouncy') // snappy, smooth, bouncy, heavy, light, elastic
// Adjust presets
import { adjustSpeed, adjustBounce } from '@oxog/springkit'
const faster = adjustSpeed(physicsPresets.modal, 1.5)
const bouncier = adjustBounce(physicsPresets.button, 0.8)
`
$3
`tsx
import { useVariants, VariantProvider } from '@oxog/springkit/react'
const cardVariants = {
initial: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
hover: { scale: 1.05 },
}
function Card() {
const { variant, setVariant, style } = useVariants(cardVariants, 'initial')
return (
style={style}
onMouseEnter={() => setVariant('hover')}
onMouseLeave={() => setVariant('visible')}
/>
)
}
`
$3
`typescript
import { createMorph, shapes } from '@oxog/springkit'
const morph = createMorph(pathElement, {
from: shapes.circle(50, 50, 40),
to: shapes.star(50, 50, 40, 20, 5),
})
await morph.play() // Morph from circle to star
await morph.reverse() // Morph back
`
$3
`tsx
import { useDrag } from '@oxog/springkit/react'
function DraggableCard() {
const [pos, api] = useDrag({
bounds: { left: -100, right: 100, top: -50, bottom: 50 },
snap: {
grid: { x: 50, y: 50 }, // Snap to grid
snapOnRelease: true,
},
})
return translate(${pos.x}px, ${pos.y}px) }} />
}
``
Documentation
Visit springkit.oxog.dev for full documentation.
License
MIT © Ersin KOÇ