Keyframe Animation
Animate scenes with CSS keyframes and render them as GIFs, WebP, APNG, or video frames.
Takumi animates scenes two ways:
renderAnimation()produces an animatedwebp,apng, orgifdirectly.render()withtimeMsrenders one frame at a time, for structuredkeyframesor external encoders like ffmpeg.
Below is a keyframe animation rendered with ffmpeg + shiki for syntax highlighting:
How to define animations
Structured keyframes objects
Use structured keyframes when you render a specific animation frame with render() and timeMs.
render() is the only API that accepts the keyframes option; renderAnimation() does not.
A keyframes object maps each animation name to a record of selector keys. Accepted selector keys:
| Key | Position |
|---|---|
from | start of the animation |
to | end of the animation |
"<n>%" | a percentage offset, e.g. "50%" |
import { } from "takumi-js";
const = await (< ="animate-[move_1s_ease-in-out_infinite_alternate]" />, {
: 100,
: 100,
: "png",
: 500,
: {
: {
: {
: "translateX(0)",
},
"50%": {
: "translateX(60px)",
},
: {
: "translateX(120px)",
},
},
},
});CSS Stylesheets
Use stylesheet @keyframes when you want the animation to travel with the JSX tree and then render it with either renderAnimation() or render() + stylesheets.
renderAnimation() takes the scenes directly: each scene's JSX (or HTML string, or node tree) is transformed and its <style> is extracted automatically, and image resources are fetched once across every scene.
import { } from "takumi-js";
const = await ({
: 100,
: 100,
: 30,
: "webp",
: [
{
: 1000,
: (
< ="w-full h-full items-center justify-center">
<>{`
@keyframes move {
from {
transform: translateX(0);
}
to {
transform: translateX(60px);
}
}
`}</>
< ="w-10 h-10 bg-red-500 animate-[move_1s_ease-in-out_infinite_alternate]" />
</>
),
},
],
});Tailwind animation utilities
Takumi supports these Tailwind animation forms:
- Presets: see the table below.
- Arbitrary shorthand values:
animate-[move_1s_ease-in-out_infinite_alternate]
| Preset | Effect |
|---|---|
animate-none | no animation |
animate-spin | continuous rotation |
animate-ping | scale and fade out, like a radar ping |
animate-pulse | fade opacity in and out |
animate-bounce | bounce up and down |
For arbitrary values, _ is converted to a space, so:
animate-[move_1s_ease-in-out_infinite_alternate]becomes:
animation: move 1s ease-in-out infinite alternate;Takumi does not currently support Tailwind's animate-(--custom-property) form because CSS custom
property resolution for animation is not implemented.
Ways to render
The next two examples share this file.
import type { } from "takumi-js";
export const : = {
: {
: {
: "translateX(0)",
},
: {
: "translateX(60px)",
},
},
};
// `keyframes` feeds render(); the inline <style> travels with the tree for renderAnimation().
export function () {
return (
< ="w-full h-full items-center justify-center">
<>{`
@keyframes move {
from {
transform: translateX(0);
}
to {
transform: translateX(60px);
}
}
`}</>
< ="w-10 h-10 bg-red-500 animate-[move_1s_ease-in-out_infinite_alternate]" />
</>
);
}render()
Use render() when you want a single frame at a specific animation time.
import { } from "takumi-js";
import { , } from "./scene";
const = await (< />, {
: 100,
: 100,
: "png",
,
: 500,
});renderAnimation()
renderAnimation() has no keyframes option. Animate the scene with stylesheet keyframes (an inline <style>, as in Scene above) or Tailwind's animation presets. scenes is an array so you can stitch several into one animation, but Takumi does not tween between them.
import { } from "takumi-js";
import { } from "./scene";
const = await ({
: 100,
: 100,
: 30,
: "webp",
: [{ : 1000, : < /> }],
});render() + ffmpeg
The ffmpeg-keyframe-animation example renders raw frames with render() and streams them into ffmpeg. Use it for video output.
Build the scene once, render each frame at a specific timeMs, and pipe the raw RGBA frames into ffmpeg.
import { } from "takumi-js";
import { } from "bun";
import { , } from "./scene";
const = 30;
const = 4;
const = 1200;
const = 630;
const = * ;
const = (
[
"ffmpeg",
"-y",
"-f",
"rawvideo",
"-pixel_format",
"rgba",
"-video_size",
`${}x${}`,
"-framerate",
`${}`,
"-i",
"pipe:0",
"output.mp4",
],
{ : "pipe", : "ignore", : "ignore" },
);
const = < />;
for (let = 0; < ; ++) {
const = ( / ) * 1000;
const = await (, {
,
,
: "raw",
,
,
});
..();
}
..();
await .;Last updated on