@ effing/annie (英语)
为动画地层生成并读取PNG,JPEG或QOI框架的TR档案.
部分 财源 家庭——用TypeScript制作节目视频.
Annie是一个简单的动画格式:一个包含相继命名的PNG,JPEG或 问 题 边框。 生成帧服务器侧,将其流到浏览器或FFmpeg上,并用同一套包回读.
安装
npm install @effing/annie
概念
安妮格式
Annie是一个TR档案,每个条目都是PNG,JPEG或QOI框架:
frame_00000
frame_00001
frame_00002
...
框架在像素格式,通道(有或没有α)和大小上可能有所不同,但一个阴极的所有框架必须是一个格式:所有PNG,所有JPEG或所有QOI. FFS和FFmpeg用解码器解码了它的第一帧.
该格式为:
- 可流性 ——帧在生成时可以被写入
- 通用 - 在所有地方都支持第三次评估报告
- FFmpeg 兼容 —— 可作为图像输入直接管道到FFmpeg
生成模式
安妮支持两代模式:
缓冲 ——收集所有帧,返回完整归档:
const archive = await annieBuffer(frames);
流线 作为帧的 Yield 块生成:
const stream = annieStream(frames);
快速启动
import { annieStream, annieBuffer } from "@effing/annie";
import { createCanvas, renderReactElement } from "@effing/canvas";
import { tween, easeOutQuad } from "@effing/tween";
// Define a frame generator
async function* generateFrames() {
yield* tween(90, async ({ lower: progress }) => {
const scale = 1 + 0.3 * easeOutQuad(progress);
const canvas = createCanvas(1080, 1920);
const ctx = canvas.getContext("2d");
await renderReactElement(
ctx,
<div style={{
width: 1080,
height: 1920,
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: 72,
transform: `scale(${scale})`
}}>
Hello World!
</div>,
{ fonts: myFonts }
);
return canvas.encode("png");
});
}
// Stream response
const stream = annieStream(generateFrames(), { signal: request.signal });
return new Response(stream, {
headers: { "Content-Type": "application/x-tar" }
});
API 概览
职能
annieStream(frames, options?)
创建一个 ReadableStream 当框架生成时产生 TAR 块.
function annieStream(
frames: AsyncIterable<Buffer>,
options?: AnnieStreamOptions,
): ReadableStream<Buffer>;
选项 :
signal- 中止取消的签名
annieBuffer(frames)
收集所有帧并返回完整的TRA缓冲.
function annieBuffer(frames: AsyncIterable<Buffer>): Promise<Buffer>;
annieFrames(source)
Async-e透出一个现存安妮的相框,按上升指数顺序排列.
function annieFrames(source: AnnieSource): AsyncGenerator<AnnieFrame>;
源头可以是 Uint8Array编号 :ArrayBuffer,一个可移动的字节块(例如节点.js) Readable),或网络 ReadableStream。每个框架带有 index条目 name粗线 data 字节和 a contentType 从帧的魔法字节中嗅出(存档本身没有记录图像格式): "image/png", (中文(简体) ), "image/jpeg", (中文(简体) ), "image/qoi",或 "application/octet-stream" 为了其他一切.
sniffImageContentType(bytes)
从它的主字节中检测出一个 PNG、 JPEG 或 QOI 图像, 相同的方式 annieFrames 有: 完整的八字节PNG签名, FF D8 FF 用于 JPEG,或 qoif 为QOI(在有14字节标题时,有3或4个频道和色相空间0或1). 回返 "image/png", (中文(简体) ), "image/jpeg", (中文(简体) ), "image/qoi" 或者说 undefined。 。 。 .
function sniffImageContentType(bytes: Uint8Array): ImageContentType | undefined;
实例
有快车/节点.js
import { Readable } from "stream";
import { annieStream } from "@effing/annie";
app.get("/animation.tar", async (req, res) => {
const stream = annieStream(generateFrames());
res.setHeader("Content-Type", "application/x-tar");
Readable.fromWeb(stream).pipe(res);
});
使用回放路由器/ 重混
import { annieStream } from "@effing/annie";
export async function loader({ request }: LoaderFunctionArgs) {
const stream = annieStream(generateFrames(), { signal: request.signal });
return new Response(stream, {
headers: { "Content-Type": "application/x-tar" },
});
}
保存到文件
import { writeFile } from "fs/promises";
import { annieBuffer } from "@effing/annie";
const buffer = await annieBuffer(generateFrames());
await writeFile("animation.tar", buffer);
读安妮
import { readFile } from "fs/promises";
import { annieFrames } from "@effing/annie";
for await (const frame of annieFrames(await readFile("animation.tar"))) {
console.log(frame.name, frame.contentType, frame.data.length);
}
FFmpeg 整合
注意Annie TAR档案可以直接被管道到FFmpeg. 提取帧用 tar -xO 和管道到FFmpeg的图像2pipe输入. 当每个帧都有相同的格式时,这个功能就起作用了;QOI帧需要FFmpeg 5.1或更新.
# Create animated PNG (loops forever)
tar -xO < animation.tar | ffmpeg -f image2pipe -framerate 30 -i - -plays 0 -c:v apng -f apng output.png
# Create MP4 video
tar -xO < animation.tar | ffmpeg -f image2pipe -framerate 30 -i - -c:v libx264 -pix_fmt yuv420p output.mp4
# Create GIF
tar -xO < animation.tar | ffmpeg -f image2pipe -framerate 30 -i - output.gif
相关软件包
@effing/tween用于帧生成的分步迭代和放松功能@effing/canvas– 将 JSX 渲染到一个PNG, JPEG 或 QOI 图像中,每个帧的图像@effing/annie-player– 在浏览器中播放安妮斯@effing/effie——用安妮斯作为视频构成的分层