用途
loop-cover 用于把一张新闻邮件或博客封面变成无缝循环的 GIF,规格为 896 x 640、180 帧、20fps(9 秒)。它的目标是在满是静态图的动态流中让封面被注意到,同时保证动作幅度小、循环不跳帧、第 1 帧单独看仍然成立。
主要能力
- 只让用户明确指定的一个元素运动,例如“灯塔的光束”“橙色线条自己画出来”“单元格之间的线”。
- 支持两种输入:已完成的静态封面(PNG),或在 HTML 中构建的封面。
- 对 HTML 封面:只对指定元素做纯时间函数动画,加入 window.seek(seconds),并捕获 180 帧、20fps。
- 对图片封面:在代码中为静态图做动画,按颜色为指定元素生成遮罩(例如蓝色明显强于红色的所有像素),将遮罩边缘柔化几个像素,逐帧只改变遮罩内的像素,遮罩外不动。
- 循环采用一个干净的正弦周期,或一次绘制—保持—淡出,跨越 9 秒,使最后一帧流入第一帧。
- 旋转循环,使第 1 帧就是完整成品画面;第 i 帧绘制时刻 ((i + offset) mod 180) / 20 秒,offset 把完全绘制状态放在最前。
- 使用 ffmpeg 编码,且不做抖动(dither=none),因为抖动会给平面图像的每一帧增加闪烁。
使用方式
开始前:如果存在 brand.md 和 MOTION.md 就先阅读;如果没有,先运行 brand-intake,或索取十六进制色值、字体和 logo 文件。缺少这些就不应称结果符合品牌。
第 1 步:询问三件事,然后等待。
- 封面:已完成的静态图(PNG),或 HTML 构建的封面。896 x 640,或保持 7:5 形状的更大图片。
- 明确说出要运动的那一个元素。
- 它如何运动:脉冲、绘制、缓慢漂移或发光。只做一个动作,不要三个。
第 2 步:构建。 HTML 封面:只对指定元素做纯时间函数动画,加入 window.seek(seconds),捕获 180 帧、20fps(9 秒)。 图片封面:在代码中为静态图做动画,按颜色生成指定元素的遮罩,柔化边缘几个像素,逐帧只改这些像素。 两者都使用一个正弦周期或一次绘制—保持—淡出,使末帧衔接首帧。 旋转循环,使第 1 帧为成品画面。 编码命令: ffmpeg -y -framerate 20 -i f%03d.png -filter_complex "split[x][y];[x]palettegen=max_colors=128:stats_mode=full[p];[y][p]paletteuse=dither=none" -loop 0 cover.gif
第 3 步:测量。 ffmpeg -v error -y -i cover.gif -vf scale=216:154,format=gray -f rawvideo frames.raw python3 -c " n = 216 * 154 d = open('frames.raw', 'rb').read() f = [d[i:i + n] for i in range(0, len(d), n)] diff = lambda a, b: sum(abs(x - y) for x, y in zip(a, b)) / n print('frames', len(f)) print('seam', round(diff(f[-1], f[0]), 2)) print('excursion', round(max(diff(x, f[0]) for x in f), 2)) "
参考目标(来自已交付封面):180 帧,接缝 0.0 到 0.3,偏移约 0.7 到 2.8,文件 0.6 到 2.3 MB。偏移接近零意味着没有可见运动。
适用场景
- 新闻邮件封面、博客封面需要以动态形式出现在信息流、收件箱或链接预览中。
- 希望封面在静态展示时(第 1 帧)仍然完整可读。
- 需要严格控制运动范围、只动一个元素的品牌化封面。
限制与注意事项
- 只应动画被指定的元素;早期版本曾让一条带扫过整个画面,导致“你把图像整个改了”。
- 视频模型会加入镜头运动,即使被告知“镜头完全静止”仍会推近;在代码中为静态图做动画是锁定镜头的唯一方式。
- 不要在中间帧测量运动:正弦在中间回到零,正常动画会报告没有运动。应测量相对第 0 帧的最大变化。
- 在 ffmpeg 的 eq 滤镜中要加 eval=frame,否则表达式只运行一次,什么都不会动。
- 缩到 240px 宽再看一遍,这是收件箱中的尺寸;如果运动元素消失,说明它太小。
- 上传 GIF 本身;有些上传工具会转成 PNG,动画会静默消失。检查上传后的链接是否仍以 .gif 结尾。
- 资料未提供收费、价格、平台支持范围等信息。