Keyframe Animation
Render CSS keyframe animations as GIF, animated WebP, APNG, or raw frames for ffmpeg.
Takumi samples CSS animations at a point in time. Pick the call by the output you need:
| Output | Call |
|---|---|
| Animated WebP, APNG, or GIF | renderAnimation() |
| One frame at a given time | render() with timeMs |
| Video through ffmpeg | render() with timeMs and format: "raw" |
This video was rendered frame by frame and encoded with ffmpeg. Its code is highlighted with shiki.
Define an animation
With a stylesheet
Write @keyframes in a <style> tag inside the tree. render() and renderAnimation() both pick it up, so the animation travels with the component.
export function () {
return (
< ="flex 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]" />
</>
);
}The next examples render this Scene.
As an object
When the values come from application data, pass the animation as a css entry. Takumi checks every value before parsing, so nothing is concatenated into a CSS string.
import { } from "takumi-js";
const = await (< ="animate-[move_1s_ease-in-out_infinite_alternate]" />, {
: 100,
: 100,
: 500,
: {
: "move",
: [
{ : "from", : { : "translateX(0)" } },
{ : "50%", : { : "translateX(60px)" } },
{ : "to", : { : "translateX(120px)" } },
],
},
});Each step takes an offset:
| Offset | Position |
|---|---|
from | Start of the animation |
to | End of the animation |
"<n>%" | A percentage offset, e.g. "50%" |
The keyframes render option takes the same animation as a nested record. It is deprecated and will be removed in v3.
With Tailwind utilities
Takumi supports these Tailwind presets:
| 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 |
An arbitrary value turns each _ into a space. animate-[move_1s_ease-in-out_infinite_alternate] becomes:
animation: move 1s ease-in-out infinite alternate;A named utility reads its theme variable, as in Tailwind. animate-wiggle reads var(--animate-wiggle), so declare that variable and its @keyframes. See Design tokens.
Takumi does not parse the animate-(--my-animation) form. Use a named utility with its
--animate-* variable instead.
Render the animation
One frame with render()
Pass timeMs to render the frame at that point of the timeline.
import { } from "takumi-js";
import { } from "./scene";
const = await (< />, {
: 100,
: 100,
: 500,
});An animated image with renderAnimation()
renderAnimation() samples each scene at fps and encodes the frames. It converts each scene's JSX, HTML string, or node tree, extracts its <style> tags, and fetches images once across every scene.
import { } from "takumi-js";
import { } from "./scene";
const = await ({
: 100,
: 100,
: 30,
: "webp",
: [{ : 1000, : < /> }],
});scenes plays several scenes in a row. Each scene starts its own timeline at 0, and Takumi does not tween between scenes.
Video with ffmpeg
Render each frame with format: "raw" and pipe the RGBA bytes into ffmpeg. The ffmpeg-keyframe-animation example is a complete version.
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 = await (, {
,
,
: "raw",
: ( / ) * 1000,
});
..();
}
..();
await .;Last updated on