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)";
}
}