Documentation menu
Guides
SVG
SVG elements take the same animate call as HTML elements. Attributes animate by name, viewBox animates as four numbers, and three extra values draw a path's stroke.
Attributes
Pass an SVG attribute by its camelCase name: cx, cy, r, rx, x1, width, fill, stroke, strokeWidth, fillOpacity and so on. animaly writes them as attributes, so they work on every SVG element that has them.
import { animate } from "@animaly/dom";
animate(".dot", { cx: 160, cy: 60 }, { type: "spring" });import { animate } from "@animaly/dom";
animate(".dot", { r: [16, 40, 24] }, { duration: 0.8 });import { animate } from "@animaly/dom";
animate(".bar", { width: 180, rx: 10 }, { duration: 0.6, ease: "easeOut" });Fill and stroke
fill and stroke take hex, rgb() or rgba() colors, like every other color in animaly.
import { animate } from "@animaly/dom";
animate(".dot", { fill: "#ec4899", stroke: "#7c3aed" }, { duration: 0.4 });import { animate } from "@animaly/dom";
const dot = document.querySelector<SVGCircleElement>(".dot")!;
dot.addEventListener("pointerenter", () => animate(dot, { strokeWidth: 6 }, { duration: 0.15 }));
dot.addEventListener("pointerleave", () => animate(dot, { strokeWidth: 2 }, { duration: 0.15 }));Transforms and opacity
x, y, rotate, scale and opacity work on SVG elements as on HTML. SVG elements rotate around the origin of the drawing by default; set transform-box: fill-box and transform-origin: center in CSS to rotate around the element itself.
import { animate } from "@animaly/dom";
// transform-box: fill-box in CSS makes the element rotate around its own center.
animate(".square", { rotate: 90, scale: 1.3 }, { type: "spring", stiffness: 200, damping: 12 });import { animate } from "@animaly/dom";
animate(".icons", { opacity: [0, 1] }, { duration: 0.4 });viewBox
viewBox animates its four numbers, which pans and zooms the drawing without touching its content.
import { animate } from "@animaly/dom";
animate(".map", { viewBox: "250 30 100 100" }, { duration: 0.8, ease: "easeInOut" });import { animate } from "@animaly/dom";
animate(".strip", { viewBox: ["0 0 200 100", "600 0 200 100"] }, { duration: 2 });Drawing paths
Three values control which part of a stroke is visible. They work on any element with a stroke: paths, lines, circles, rectangles and polylines.
pathLengthis the visible part of the stroke, from 0 (nothing) to 1 (all of it).pathSpacingis the gap after each visible part, also as a fraction of the path. It repeats the dash along the path.pathOffsetmoves where the visible part starts.
import { animate } from "@animaly/dom";
// pathLength 0 hides the stroke, 1 shows all of it.
animate(".line", { pathLength: [0, 1] }, { duration: 1.2, ease: "easeInOut" });import { animate } from "@animaly/dom";
animate(".line", { pathLength: [1, 0] }, { duration: 0.6, ease: "easeIn" });import { animate } from "@animaly/dom";
// Each dash is 0.1 of the path long, followed by a 0.1 gap.
animate(".line", { pathLength: 0.1, pathSpacing: 0.1 });import { animate } from "@animaly/dom";
animate(".line", { pathLength: 0.2, pathOffset: [0, 0.8] }, {
duration: 1.5,
repeat: Infinity,
repeatType: "reverse",
});animaly sets the pathLength attribute to 1 and writes stroke-dasharray and stroke-dashoffset for you, so the values are fractions whatever the real length of the path.
Common patterns
A checkmark
Await the ring, then draw the tick.
import { animate } from "@animaly/dom";
await animate(".ring", { pathLength: [0, 1] }, { duration: 0.5, ease: "easeOut" });
animate(".tick", { pathLength: [0, 1] }, { duration: 0.3, ease: "easeOut" });A progress ring
Animate pathLength on a circle to the progress value. A spring makes each update glide from wherever the ring is.
import { animate } from "@animaly/dom";
const setProgress = (value: number) =>
animate(".meter", { pathLength: value }, { type: "spring", stiffness: 120, damping: 20 });
setProgress(0.65);import { animate } from "@animaly/dom";
const percent = document.querySelector<HTMLOutputElement>(".percent")!;
animate(".meter", { pathLength: [0, 0.8] }, {
duration: 1.2,
ease: "easeOut",
onUpdate: (latest, name) => {
if (name !== "pathLength") return;
percent.textContent = `${Math.round(Number(latest) * 100)}%`;
},
});Morphing between numeric attributes
Any numeric attribute can move, so lines, ellipses and rectangles can change into each other's positions and proportions.
import { animate } from "@animaly/dom";
animate(".beam", { x1: 100, y1: 10, x2: 100, y2: 90 }, { type: "spring", stiffness: 180, damping: 14 });import { animate } from "@animaly/dom";
animate(".blob", { rx: [40, 80, 40], ry: [40, 24, 40] }, { duration: 0.9, repeat: 2 });Several paths in turn
import { animate, stagger } from "@animaly/dom";
animate(".stroke", { pathLength: [0, 1] }, { duration: 0.4, delay: stagger(0.12) });import { animate, stagger } from "@animaly/dom";
animate(".bead", { cy: [30, 12, 30] }, { duration: 0.6, delay: stagger(0.08, { from: "center" }), repeat: Infinity });import { animate } from "@animaly/dom";
await animate(".mark", { pathLength: [0, 1] }, { duration: 1, ease: "easeInOut" });
animate(".mark", { fillOpacity: 1 }, { duration: 0.4 });