Loading...
Loading...
This skill should be used when the user asks to "add a Lottie animation", "play a .lottie or .json animation", "integrate a Bodymovin/After Effects animation on web or mobile", "control Lottie playback / segments", "make a scroll-driven Lottie", "recolor a Lottie at runtime", or "export an AE animation to Lottie". Covers dotLottie/lottie-web integration, playback control, interactivity, theming, and the AE export checklist.
npx skill4agent add iart-ai/web-animation-skills lottie-animation.lottie.json.lottie@lottiefiles/dotlottie-weblottie-weblottie-web.jsonnpm i @lottiefiles/dotlottie-web # vanilla
npm i @lottiefiles/dotlottie-react # React wrapperimport { DotLottie } from "@lottiefiles/dotlottie-web";
const dotLottie = new DotLottie({
canvas: document.querySelector("#lottie"), // a <canvas> element
src: "/hero.lottie", // or .json
loop: true,
autoplay: true,
// speed: 1, mode: "forward" | "reverse" | "bounce", renderConfig: { autoResize: true }
});import { DotLottieReact } from "@lottiefiles/dotlottie-react";
function Hero() {
return <DotLottieReact src="/hero.lottie" loop autoplay style={{ width: 320 }} />;
}const [dl, setDl] = useState(null);
<DotLottieReact src="/icon.lottie" dotLottieRefCallback={setDl} />;
// dl?.play(); dl?.pause(); dl?.setSpeed(2);dotLottie.play();
dotLottie.pause();
dotLottie.stop();
dotLottie.setSpeed(1.5);
dotLottie.setMode("bounce"); // forward | reverse | bounce | reverse-bounce
dotLottie.setFrame(42); // jump to a frame
dotLottie.setSegment(30, 90); // constrain playback to a frame range (e.g. a "loading" loop)dotLottie.addEventListener("load", () => { /* totalFrames now available */ });
dotLottie.addEventListener("complete", () => { /* non-looping playback finished */ });
dotLottie.addEventListener("frame", ({ currentFrame }) => { /* per-frame */ });completesetSegmentconst dotLottie = new DotLottie({ canvas, src: "/scroll.lottie", autoplay: false });
let total = 0;
dotLottie.addEventListener("load", () => { total = dotLottie.totalFrames; });
window.addEventListener("scroll", () => {
const el = canvas.closest(".scrolly");
const rect = el.getBoundingClientRect();
const p = clamp(-rect.top / (rect.height - window.innerHeight), 0, 1); // 0..1 through the section
dotLottie.setFrame(p * (total - 1));
}, { passive: true });
const clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));@lottiefiles/lottie-interactivityscrollhoverclickplaycursor.lottiedotLottie.setTheme("dark")loadTheme(...)layers[].shapes[]c.klottie-webrendererSettingslottie-iosLottieAnimationView.play().currentProgress.play(fromFrame:toFrame:)lottie-androidLottieAnimationViewLottieAnimationsetMinAndMaxFrameprogresslottie-react-native<LottieView source={...} progress={anim} />progressAnimated.lottie.json.lottie.jsonIntersectionObserverplaypausePackaged helper ():scripts/freezes thescripts/seek-shot.sh anim.html 0 1.5 3harness and screenshots each moment;?t=Ntiles them for one-glance review. Seescripts/contact-sheet.sh sheet.png frame-*.png.scripts/README.md
<canvas>.lottie.json.html@lottiefiles/dotlottie-webautoplay:false?t=N<script>
const dl = new DotLottie({ canvas, src: "/icon.lottie", autoplay: false });
dl.addEventListener("load", () => {
const t = new URLSearchParams(location.search).get("t");
if (t !== null) dl.setFrame(parseFloat(t)); // freeze on frame N (lottie-web: anim.goToAndStop(N, true))
else dl.play();
console.log("totalFrames", dl.totalFrames); // read for end-frame
window.__ready = true;
});
</script>?t=0?t=<total/2>?t=<total-1>totalFramesautoResizenpx playwright screenshot --wait-for-timeout=600 "file://$PWD/lottie.html?t=42" frame-mid.png.lottie.json?t=NsetFramegoToAndStopprefers-reduced-motionautoplay| Goal | Call |
|---|---|
| Play a range | |
| Jump to frame | |
| Reverse / bounce | `setMode("reverse" |
| Scroll-drive | |
| Theme swap | |
| Know length | |
| Declarative interactivity | |
references/integration-and-export.mdlottie-interactivity