动图GIF
三个工具,同一组装核心。根据输入类型选择对应工具。
| 输入 | 工具 | 运动方式 |
|---|---|---|
| 带ANSI颜色的文本帧 | bin/ascii_gif.py | 帧序列 |
| 照片或图片文件夹 | bin/pics_gif.py | 停留、交叉淡入淡出、肯伯恩斯效果 |
| SVG或HTML动画 | bin/svg_gif.py | SMIL、CSS或预设旋转/脉冲/淡入 |
所有工具共享 bin/gifkit.py:整个循环使用同一调色板、10毫秒时间网格、相邻重复帧合并为较长延迟、原子写入、读取后校验。
依赖项
Pillow必须安装。SVG和HTML需要Node.js环境(含Playwright和Chromium无头浏览器)。
python -c "import PIL; print(PIL.__version__)"
node -e "require('playwright')"
npx playwright install chromium --only-shell
当依赖项或输入缺失时,每个工具会在stderr上打印一行 FAIL ... 并退出码为1。不要使用相同参数重试。
工作流程
- 根据表格选择对应工具,阅读下方对应的章节。
- 运行工具,指定输出路径到正在处理的项目目录内,不要写入系统临时目录或桌面。
- 保留打印摘要:帧数、时长、像素尺寸。路径被打印即表示GIF已验证。
- 对于视觉效果,打开GIF或使用
--preview preview.png并检查静态图片后再报告。 - 报告绝对路径以及相关设置:fps、尺寸、时长、模式。
ASCII文本帧路线
输入为文本文件。帧与帧之间由仅包含 --- 的行分隔;可在分隔符后添加可选延时的毫秒数:--- 250。支持 ANSI SGR 颜色:30-37、90-97、38;5;n 和 38;2;r;g;b。.txt 文件目录按文件名排序,每个文件作为一帧。
python bin/ascii_gif.py frames.txt -o project/loader.gif --size 28 --delay 120
python bin/ascii_gif.py frames/ -o project/loader.gif --preview project/loader.png
--font必须是等宽TTF或TTC字体;省略时自动检测常见等宽字体。--width和--height固定画布并将文本网格居中。两个参数要么同时给出,要么都不给。用于在不同样例间匹配尺寸。--fg和--bg默认为浅色文本配近黑画布。--delay是默认帧延迟;分隔符可覆盖每帧延迟。- 帧内容建议控制在约120列×40行以内;更大的画布会产生较长的GIF。
图片文件夹路线
文件按自然排序(pic-2 在 pic-10 之前),自动根据EXIF旋转,并调整到统一画布。
python bin/pics_gif.py photos/ -o project/reel.gif --mode kenburns --fps 12 --hold 1.2 --transition 0.5 --width 800
python bin/pics_gif.py photos/ -o project/reel.gif --mode sequence --fps 10 --hold 0.8 --fit contain --bg "#101010"
sequence模式:每张图片保持显示;crossfade模式:图片之间交叉淡入淡出;kenburns模式:每张图片缓慢缩放,方向交替变化。--width设置画布宽度;--height默认为第一张图片的长宽比。--fit cover裁剪填充,contain用--bg颜色填充空白。--hold每张图片保持的秒数,--transition混合过渡的秒数,--zoom肯伯恩斯效果的最终缩放比例。- 100张照片,800像素宽,12 fps会产生较大文件;先尝试减小宽度或降低fps。
SVG和HTML路线
该工具将 .svg 包裹在指定视口的页面中,或直接打开 .html 文件,然后暂停并在每次截图前将每个动画定位到精确时间点。帧不依赖于真实时钟时间。
python bin/svg_gif.py logo.svg -o project/logo.gif --duration 3 --fps 15
python bin/svg_gif.py logo.svg -o project/logo.gif --animate rotate --selector "#mark" --duration 2 --background "#0b0d12"
python bin/svg_gif.py page.html -o project/page.gif --width 960 --height 540 --duration 4 --fps 12
- 不使用
--animate时,SVG或页面必须本身包含SMIL或CSS动画。静态图形需要使用预设。 - 预设:
rotate(旋转)、pulse(脉冲)、fade(淡入),应用于SVG根元素或--selector指定的元素。 --width和--height默认使用SVG的width/height或viewBox。--dpr 2以两倍视口分辨率捕获,可获得更清晰的文字;生成的GIF像素尺寸也会翻倍。- 本工具不驱动基于JS的Canvas或WebGL页面。请使用能暴露帧钩子的页面,或预先渲染帧并通过
gifkit组装。
通用选项
--colors最多255色。仅在调色板简单或需要减小文件大小时降低此值。--dither none适用于扁平艺术和文本;floyd有助于渐变和照片。--loop 0无限循环;其他值重复指定次数。--preview PATH写入第一帧的PNG静态图片。- 相同输入、设置和工具链时输出具有确定性。
自定义帧序列
任何帧序列都可以使用同一核心。帧必须是RGB且大小相同;延迟单位为毫秒,基于10毫秒网格。
import sys
sys.path.insert(0, "bin")
import gifkit
from PIL import Image
frames = [Image.new("RGB", (320, 180), (n * 20, 40, 90)) for n in range(12)]
gifkit.write_gif(frames, "project/gradient.gif", [100] * 12)
write_gif 构建统一调色板、合并相邻重复帧、原子写入并校验结果。此方法适用于数据动画、生成的图表或上述工具无法覆盖的任何帧序列。
约定
- 工具在输出目录下写入
<name>.tmp.gif,通过读取文件回校验证帧数、总时长和像素尺寸,然后重命名。失败时删除临时文件,不留未完成的输出。 - GIF使用统一调色板,因此颜色不会闪烁。
- 相邻重复帧合并为一帧并延长延迟;打印的帧数为存储的帧数。
- 延迟落在10毫秒GIF网格上,且至少20毫秒。
验证
验证检查位于源代码库中。从仓库根目录执行:
python tools/check.py
每条检查打印一行 PASS、SKIP 或 FAIL。缺失可选依赖时默认跳过;--strict 要求每条路线都通过。检查会在 scratch/checks 下生成测试数据,同时扫描仓库中是否包含密钥、绝对路径、非ASCII文本和CRLF换行符。
限制
- GIF是8位索引色,最多256色;照片若不进行抖动会显示色带。
- 查看器对短延迟有下限;帧延迟应保持至少20毫秒。
- 长时间、高帧率、真彩色序列更适合使用APNG或WebM。当需要高质量时请告知用户。
- 内存随帧数和画布尺寸增长;所有帧在构建统一调色板时都会被保留。
- 文字字形覆盖取决于所选字体;缺失的方框或块状字形会回退为空白方框。
参见 references/animated-gif-considerations.md 了解格式约束及其默认值的依据。
文件
bin/gifkit.py- 调色板、时序、合并、原子写入、验证。bin/ascii_gif.py- ANSI文本帧转GIF。bin/pics_gif.py- 图片文件夹转GIF。bin/svg_gif.py- SVG/HTML动画转GIF。bin/render_frames.cjs- Playwright帧捕获辅助脚本。references/animated-gif-considerations.md- 格式说明。
仓库检查和示例生成位于 tools/ 目录下,不属于已安装的技能负载。