A production-ready motion library for React that provides comprehensive animation and gesture APIs for creating fluid, performant interactions.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Direct DOM animation functions that work without React, for imperative animations and scroll tracking.
Direct DOM animation function using Web Animations API or fallback engine.
/**
* Animate DOM elements directly without React
* @param element - Element to animate (selector string or Element)
* @param keyframes - Animation keyframes or single keyframe object
* @param options - Animation options
* @returns Animation playback controls
*/
function animate(
element: string | Element,
keyframes: Record<string, any> | Record<string, any>[],
options?: AnimationOptions
): AnimationPlaybackControls;
/**
* Create scoped animate function bound to a container
* @param scope - Container element for scoped queries
* @returns Scoped animate function
*/
function createScopedAnimate(scope: Element): typeof animate;
interface AnimationOptions {
/**
* Animation duration in seconds
*/
duration?: number;
/**
* Delay before animation starts in seconds
*/
delay?: number;
/**
* Easing function or WAAPI easing string
*/
ease?: string | number[] | ((t: number) => number);
/**
* Number of times to repeat animation
*/
repeat?: number;
/**
* Type of repeat behavior
*/
repeatType?: "loop" | "reverse" | "mirror";
/**
* Delay between repeats in seconds
*/
repeatDelay?: number;
/**
* Direction of animation
*/
direction?: "normal" | "reverse" | "alternate" | "alternate-reverse";
/**
* Fill mode for animation
*/
fill?: "none" | "forwards" | "backwards" | "both";
/**
* Animation end behavior
*/
endDelay?: number;
/**
* Start time offset
*/
startTime?: number;
/**
* Animation timing function
*/
composite?: "replace" | "add" | "accumulate";
}
interface AnimationPlaybackControls {
/**
* Stop the animation
*/
stop(): void;
/**
* Pause the animation
*/
pause(): void;
/**
* Resume the animation
*/
play(): void;
/**
* Reverse the animation direction
*/
reverse(): void;
/**
* Cancel the animation and reset to initial state
*/
cancel(): void;
/**
* Complete the animation immediately
*/
finish(): void;
/**
* Get current animation time
*/
currentTime: number;
/**
* Get animation duration
*/
duration: number;
/**
* Get animation playback rate
*/
playbackRate: number;
/**
* Set animation playback rate
*/
updatePlaybackRate(rate: number): void;
/**
* Promise that resolves when animation completes
*/
then(onResolve?: () => void, onReject?: (error: any) => void): Promise<void>;
/**
* Attach event listeners
*/
addEventListener(event: string, handler: EventListener): void;
/**
* Remove event listeners
*/
removeEventListener(event: string, handler: EventListener): void;
}Usage Examples:
import { animate, createScopedAnimate } from "framer-motion/dom";
// Basic DOM animation
function basicDOMAnimation() {
// Animate single element
animate(".box", { x: 100, rotate: 45 }, { duration: 1 });
// Animate with keyframes
animate(".circle", [
{ scale: 1, backgroundColor: "#ff0000" },
{ scale: 1.5, backgroundColor: "#00ff00" },
{ scale: 1, backgroundColor: "#0000ff" }
], {
duration: 2,
repeat: Infinity,
repeatType: "reverse"
});
// Animate multiple elements
document.querySelectorAll(".item").forEach((element, index) => {
animate(element,
{ y: -50, opacity: [0, 1] },
{ duration: 0.5, delay: index * 0.1 }
);
});
}
// Animation with controls
function controlledDOMAnimation() {
const controls = animate(".animated-element",
{ rotate: 360 },
{ duration: 2, repeat: Infinity }
);
// Control playback
document.getElementById("pause")?.addEventListener("click", () => {
controls.pause();
});
document.getElementById("play")?.addEventListener("click", () => {
controls.play();
});
document.getElementById("reverse")?.addEventListener("click", () => {
controls.reverse();
});
document.getElementById("stop")?.addEventListener("click", () => {
controls.stop();
});
// Promise-based completion
controls.then(() => {
console.log("Animation completed");
});
}
// Scoped animations
function scopedDOMAnimation() {
const container = document.getElementById("animation-container");
if (container) {
const scopedAnimate = createScopedAnimate(container);
// Animate elements within container only
scopedAnimate(".box", { x: 200 }, { duration: 1 });
scopedAnimate(".circle", { scale: 1.5 }, { duration: 0.8 });
}
}Ultra-lightweight animation function for simple WAAPI animations.
/**
* Minimal WAAPI-based animation function
* @param element - Element to animate
* @param keyframes - Animation keyframes
* @param options - WAAPI options
* @returns Native Web Animation object
*/
function animateMini(
element: string | Element,
keyframes: Keyframe[] | PropertyIndexedKeyframes,
options?: KeyframeAnimationOptions
): Animation;
/**
* Sequence multiple animations with precise timing
* @param animations - Array of animation definitions
* @param options - Sequence options
* @returns Promise resolving when sequence completes
*/
function animateSequence(
animations: Array<[string | Element, Keyframe[] | PropertyIndexedKeyframes, KeyframeAnimationOptions?]>,
options?: SequenceOptions
): Promise<void>;
interface SequenceOptions {
/**
* Default duration for animations without explicit duration
*/
defaultDuration?: number;
/**
* Default easing for animations
*/
defaultEasing?: string;
/**
* Delay between animations
*/
stagger?: number;
}Usage Examples:
import { animateMini, animateSequence } from "framer-motion/dom-mini";
// Minimal WAAPI animation
function minimalAnimation() {
const element = document.querySelector(".element");
if (element) {
const animation = animateMini(element, [
{ transform: "translateX(0px) scale(1)" },
{ transform: "translateX(100px) scale(1.2)" },
{ transform: "translateX(0px) scale(1)" }
], {
duration: 1000,
easing: "ease-in-out",
iterations: Infinity
});
// Native Web Animation methods
animation.pause();
animation.play();
animation.addEventListener("finish", () => {
console.log("Animation finished");
});
}
}
// Animation sequences
async function sequencedAnimations() {
await animateSequence([
[".header", [{ opacity: 0 }, { opacity: 1 }], { duration: 500 }],
[".content", [{ y: 50 }, { y: 0 }], { duration: 600 }],
[".footer", [{ scale: 0.8 }, { scale: 1 }], { duration: 400 }]
], {
stagger: 100 // 100ms delay between each animation
});
console.log("Sequence completed");
}Direct DOM scroll tracking utilities that work without React.
/**
* Track scroll with callback (already documented in scroll-view.md)
*/
function scroll(
onScroll: (info: ScrollInfo) => void,
options?: ScrollOptions
): () => void;
/**
* Advanced scroll tracking with detailed information
*/
function scrollInfo(
onScroll: (info: DetailedScrollInfo) => void,
options?: ScrollOptions
): () => void;
interface ScrollInfo {
x: number;
y: number;
xProgress: number;
yProgress: number;
velocity: number;
}
interface DetailedScrollInfo extends ScrollInfo {
targetBounds: DOMRect;
containerBounds: DOMRect;
intersectionRatio: number;
}Direct DOM intersection observer utilities.
/**
* Observe element intersection with viewport (already documented in scroll-view.md)
*/
function inView(
element: string | Element,
onStart: (entry: IntersectionObserverEntry) => void | (() => void),
options?: InViewOptions
): () => void;
interface InViewOptions {
root?: Element;
margin?: string;
amount?: number | number[] | "some" | "all";
}Utilities for working with CSS transforms.
/**
* Build CSS transform string from transform object
* @param transform - Transform properties object
* @param transformOrigin - Transform origin values
* @returns CSS transform string
*/
function buildTransform(
transform: Record<string, string | number>,
transformOrigin?: string
): string;
/**
* Parse transform string into object
* @param transformString - CSS transform string
* @returns Object with transform properties
*/
function parseTransform(transformString: string): Record<string, number>;
/**
* Interpolate between transform states
* @param from - Starting transform object
* @param to - Ending transform object
* @param progress - Interpolation progress (0-1)
* @returns Interpolated transform object
*/
function interpolateTransforms(
from: Record<string, number>,
to: Record<string, number>,
progress: number
): Record<string, number>;Usage Examples:
import { buildTransform, parseTransform } from "framer-motion/dom";
// Build transform strings
function transformUtilities() {
// Build transform from object
const transformString = buildTransform({
x: 100,
y: 50,
rotate: 45,
scale: 1.2,
skewX: 10
}, "center center");
console.log(transformString);
// "translateX(100px) translateY(50px) rotate(45deg) scale(1.2) skewX(10deg)"
// Apply to element
const element = document.querySelector(".element");
if (element) {
(element as HTMLElement).style.transform = transformString;
(element as HTMLElement).style.transformOrigin = "center center";
}
// Parse existing transform
const existing = (element as HTMLElement)?.style.transform || "";
const parsed = parseTransform(existing);
console.log(parsed); // { translateX: 100, translateY: 50, rotate: 45, ... }
}Additional utilities for animation management.
/**
* Delay execution by specified time
* @param duration - Delay duration in seconds
* @returns Promise that resolves after delay
*/
function delay(duration: number): Promise<void>;
/**
* Create delayed function
* @param fn - Function to delay
* @param duration - Delay duration in seconds
* @returns Delayed function
*/
function delay<T extends (...args: any[]) => any>(
fn: T,
duration: number
): DelayedFunction<T>;
interface DelayedFunction<T extends (...args: any[]) => any> {
(...args: Parameters<T>): Promise<ReturnType<T>>;
cancel(): void;
}
/**
* RAF-based animation loop
* @param callback - Function to call each frame
* @returns Cleanup function to stop the loop
*/
function animationLoop(callback: (time: number, delta: number) => void): () => void;
/**
* Throttle function calls to animation frames
* @param fn - Function to throttle
* @returns Throttled function
*/
function throttleToAnimationFrame<T extends (...args: any[]) => any>(fn: T): T;Usage Examples:
import { delay, animationLoop, throttleToAnimationFrame } from "framer-motion/dom";
// Delay utilities
async function delayExample() {
console.log("Starting...");
await delay(1); // Wait 1 second
console.log("After 1 second");
// Delayed function execution
const delayedLog = delay(console.log, 0.5);
delayedLog("This appears after 500ms");
// Cancel delayed execution
const cancelable = delay(() => console.log("This won't run"), 2);
cancelable.cancel();
}
// Animation loop
function customAnimationLoop() {
let rotation = 0;
const element = document.querySelector(".rotating-element") as HTMLElement;
const stopLoop = animationLoop((time, delta) => {
rotation += delta * 0.1; // Rotate based on time delta
if (element) {
element.style.transform = `rotate(${rotation}deg)`;
}
});
// Stop after 5 seconds
setTimeout(stopLoop, 5000);
}
// Throttled event handling
function throttledScrollExample() {
const handleScroll = throttleToAnimationFrame((event: Event) => {
const scrollY = window.scrollY;
const header = document.querySelector(".header") as HTMLElement;
if (header) {
header.style.transform = `translateY(${Math.min(0, -scrollY * 0.5)}px)`;
}
});
window.addEventListener("scroll", handleScroll);
// Cleanup
return () => window.removeEventListener("scroll", handleScroll);
}Combining with React:
import { animate } from "framer-motion/dom";
import { useEffect, useRef } from "react";
function HybridAnimation() {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
if (ref.current) {
// Use DOM animate for complex sequences
const controls = animate(ref.current, [
{ x: 0, y: 0 },
{ x: 100, y: 0 },
{ x: 100, y: 100 },
{ x: 0, y: 100 },
{ x: 0, y: 0 }
], {
duration: 4,
repeat: Infinity,
ease: "linear"
});
return () => controls.stop();
}
}, []);
return <div ref={ref} className="w-16 h-16 bg-blue-500" />;
}Performance Optimization:
// Batch DOM animations
function batchAnimations() {
const elements = document.querySelectorAll(".animate-me");
// Start all animations simultaneously for better performance
elements.forEach((element, index) => {
animate(element,
{ y: -100, opacity: [0, 1] },
{
duration: 0.6,
delay: index * 0.05, // Small stagger
ease: "easeOut"
}
);
});
}
// Memory-efficient cleanup
function animationWithCleanup() {
const cleanupFunctions: (() => void)[] = [];
// Store cleanup functions
cleanupFunctions.push(
scroll((info) => {
// Handle scroll
})
);
cleanupFunctions.push(
inView(".target", (entry) => {
// Handle intersection
})
);
// Clean up all listeners
return () => {
cleanupFunctions.forEach(cleanup => cleanup());
};
}Progressive Enhancement:
// Feature detection and fallbacks
function progressiveAnimation() {
const element = document.querySelector(".element") as HTMLElement;
if ("animate" in document.createElement("div")) {
// Use Web Animations API
animate(element, { x: 100 }, { duration: 1 });
} else {
// Fallback to CSS transitions
element.style.transition = "transform 1s ease";
element.style.transform = "translateX(100px)";
}
}