個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證CSS to Lottie converter: turn HTML + CSS animations (keyframes, transitions) and inline SVG into Lottie JSON. No After Effects needed.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Write animations in HTML + CSS, get a Lottie file. No After Effects needed.
Live demo: see every example as CSS and Lottie, side by side
css2lottie converts CSS @keyframes, transitions and inline SVG into Lottie JSON (Bodymovin 5.7+). The output plays in lottie-web, lottie-ios, lottie-android and Flutter.
npx css2lottie spinner.html -o spinner.json
ease, ease-in-out, cubic-bezier(...)) can be reproduced exactly, you get one Lottie keyframe with matching bezier handles instead of 60 keyframes per second. It falls back to per-frame sampling only where needed.box-shadow, filters or gradients.npm install --save-dev css2lottie
npx css2lottie install-browser # one-time: downloads the headless Chromium used for sampling
Requires Node.js 18.17+. If you already have Chrome/Chromium, you can skip install-browser and pass --executable-path (or set CSS2LOTTIE_CHROMIUM_PATH).
npx css2lottie input.html -o output.json --width 400 --height 400 --fps 60
| Flag | Description |
|---|---|
-o, --output <file> | Output file (default <input>.json, - for stdout). A .lottie name writes dotLottie |
--width, --height | Composition size in px (default: the root element's size with --selector, else 512) |
--fps <n> | Frame rate (default 60) |
-d, --duration <ms|s> | Duration (default: auto-detected from the longest animation; one loop for infinite) |
-s, --selector <css> | Convert only this root element (centered in the composition) |
--report | Print unsupported / approximated features |
--report-json <file> | Write the report as JSON |
--preview | Open a local page that plays the CSS original and the Lottie side by side |
-w, --watch | Re-convert on every save; with --preview the page reloads by itself |
--no-optimize | Disable easing optimization (per-frame keys, simplified) |
--font <family=file> | Font file used to convert text to outlines (repeatable) |
--background | Include the page background color as a layer |
--pretty | Pretty-print the JSON |
--executable-path <path> | Chromium/Chrome binary to use |
--preview starts a small local server and opens a comparison page. It has a shared scrubber, so you can step both versions to the same frame.
import { convert } from "css2lottie";
const lottie = await convert({ html, width: 400, height: 400, fps: 60 });
To get the report as well:
import { convertWithReport, formatReport } from "css2lottie";
const { lottie, report } = await convertWithReport({
file: "./animations/logo.html", // or `html` (string) / `url`
selector: "#logo", // optional: one root element
duration: 2000, // optional: ms (auto-detected by default)
fonts: { Inter: "./fonts/Inter-Bold.ttf" }, // optional: text → outlines
});
console.log(formatReport(report));
| Option | Default | Description |
|---|---|---|
html / file / url | — | Source document (one is required) |
baseUrl | — | Resolves relative URLs when passing an html string |
width, height | root size or 512 | Composition size |
fps | 60 | Frame rate |
duration | auto | Duration in ms |
selector | body | Root element to convert |
optimizeKeyframes | true | Emit bezier-eased keyframes when they reproduce the samples exactly |
fonts | {} | family → file map for text outlines |
background | false | Add the page background as a layer |
browser | launched per call | Reuse a Playwright Browser for batch conversions |
executablePath | Playwright's Chromium | Custom Chromium/Chrome binary |
Batch conversions can reuse one browser:
import { convert, launchBrowser } from "css2lottie";
const browser = await launchBrowser();
for (const file of files) await convert({ file, browser });
await browser.close();
Write a dotLottie (.lottie) file instead of JSON. It is a zip that the LottieFiles players and the mobile runtimes load directly; embedded images are stored as files, so it is usually much smaller:
import { writeFile } from "node:fs/promises";
import { convert, toDotLottie } from "css2lottie";
const lottie = await convert({ file: "./spinner.html" });
await writeFile("spinner.lottie", toDotLottie(lottie, { id: "spinner", loop: true }));
From the CLI, just pick a .lottie output name: npx css2lottie spinner.html -o spinner.lottie.
npx css2lottie anim.html --watch --preview converts on every save (of the HTML or any CSS, image or font next to it) and reloads the side-by-side preview, so you can tweak keyframes and see the Lottie update right away.
| CSS / SVG | Lottie output | Notes |
|---|---|---|
transform (translate, rotate, scale, skew, matrix), translate / rotate / scale | Layer position / rotation / scale / skew, anchor = transform-origin | 3D transforms are flattened to 2D, and backface-visibility is respected |
opacity, visibility | Layer opacity | Multiplied down the tree (Lottie parents don't pass opacity on) |
background-color | Fill | |
border (width, color per side) | Stroke, trimmed strokes (circles), or trapezoid fills | Rounded boxes with different sides are approximated |
border-radius | Rect roundness, ellipse, or a per-corner path | Elliptical and per-corner radii supported |
width / height / layout (left, top, margin, …) | Rect size + layer position | |
SVG <rect>, <circle>, <ellipse>, <path>, <polygon>, <polyline>, <line>, <g> | Shape layers with bezier paths, parented to the <svg> viewBox | Arcs, quadratics and relative commands are converted to cubics |
SVG fill, stroke, stroke-width, *-opacity, stroke-linecap/join, fill-rule | Fill / stroke | |
d: path(...) animation | Animated path | Needs the same number of points on every frame |
| Nested elements | Layer parenting | |
::before / ::after | Shape layers | Need to be absolutely positioned in a positioned host |
<img> | Image layer + base64 asset | |
| Text | Glyph outlines (shapes) | Needs a TTF/OTF/WOFF font file via @font-face or |
Some CSS has no Lottie equivalent yet. These features are ignored or approximated, and each one is listed in the report:
box-shadow, filter, backdrop-filter, mix-blend-mode, text-shadowlinear-gradient / radial-gradient and background images; SVG gradients and patternsclip-path, masks, and overflow: hidden clippingstroke-dasharray / stroke-dashoffset, so the "path drawing" effect doesn't carry overperspective: 3D is flattened to 2DrequestAnimationFrame, timers, scroll) and transitions triggered later than page loadSee ROADMAP.md for what's next. If the converter gets something wrong, please open an "Unsupported animation" issue and attach your HTML.
document.getAnimations() (CSS animations, transitions and Web Animations) are paused. Their timing and keyframes are recorded.animation.currentTime. The sampler records computed styles, the composed transform matrix, transform-origin and the untransformed layout box.src/converters/ map property families to Lottie data.examples/ contains 11 animations: spinner, bouncing ball, pulse, logo reveal, loading dots, checkmark, card flip, progress bar, heart beat, notification bell and text wave. Regenerate their Lottie files with npm run build && npm run examples. They also play on the live demo site, which npm run build && npm run site:serve builds and serves locally. The test suite converts each one, validates it against the Lottie JSON schema, checks it against a snapshot and compares lottie-web renders with the CSS renders.
css2lottie is free and MIT-licensed. If it saves you an After Effects license or a designer round-trip, please become a sponsor on GitHub. Our goal is $5,000 a month so css2lottie can be maintained full time and ship gradients, masks and path drawing sooner.
github.com/sponsors/99proteam (monthly or one-time) · One-off coffees are welcome too: buymeacoffee.com/99proteam
PRs are welcome. CONTRIBUTING.md explains how to add support for a new CSS property; each converter is one small file with a test.
MIT. Example font: Instrument Sans (SIL Open Font License, see examples/fonts/).
fonts@keyframes, transitions triggered on load, element.animate() | Keyframes | delay, iteration-count, direction, fill-mode and playback rate |
ease, ease-in, ease-out, ease-in-out, cubic-bezier(), linear | Bezier keyframe easing | steps() and linear(...) stops are sampled per frame |
animation-iteration-count: infinite | A seamless loop | The duration is the LCM of the loop periods, starting from the steady state |