GIF 动图怎么做?拆帧、合成与体积控制
动图处理的坑集中在三处:GIF 只支持 256 色、允许一帧只存变化区域、以及浏览器对极短帧延迟的处理。知道这三点,拆帧与合成的结果就都可预期。
- GIF 只有 256 色,量化到不超过 255 色还要留一个透明槽,所以渐变和照片必然出现色带
- 每帧独立量化,相邻帧调色板不一致时播放中会有轻微闪烁,纯色或简单图形做的帧则平滑得多
- 拆帧不是把每帧原样掏出来:GIF 允许一帧只存变化区域,必须按 Disposal Method 逐帧合成才是完整画面
- 「自动」尺寸取所有帧的最大宽高且不缩放,随手混进一张大图会把整段 GIF 撑到那个尺寸
- 浏览器普遍把小于 20 毫秒的帧延迟按 100 毫秒处理,把延迟写成 10ms 没有意义,只能减帧或降尺寸
#三个限制决定了做法
动图处理的所有取舍都来自三条硬限制:颜色只有 256 色、帧可以只存变化区域、浏览器对极短延迟有自己的一套处理。 不了解这三条,遇到色带、缺块、卡顿都会误以为是工具坏了。
| 限制 | 直接后果 | 应对 |
|---|---|---|
| 256 色上限 | 渐变与照片出色带 | 用纯色或简单图形做帧 |
| 帧可只存差异 | 拆帧必须逐帧合成 | 用支持规范的拆帧工具 |
| 极短延迟被改写 | 20ms 以下没有意义 | 减帧或降尺寸来提速 |
#拆帧:为什么不能原样掏出
GIF 允许一帧只保存与上一帧的差异区域,所以「把每帧原样掏出来」得到的会是一堆缺了一块的补丁。 必须按规范逐帧合成,才能还原每一帧的完整画面。
判断是否需要合成,看 Disposal Method(画完这一帧怎么处理):
- 保留:后面的帧继续叠在它上面
- 恢复背景:画完擦掉,用背景色铺底
- 恢复上一帧:画完退回上一帧的状态
用 GIF 帧提取器 时,它会叠加增量帧、处理透明索引、按 disposal 还原或保留,必要时用逻辑屏幕描述符里的背景色铺底,所以导出的每一张都是完整画面。
导出 JPEG 时透明区域会变黑 —— Canvas 转 JPEG 不铺底色,而 PNG 与 WebP 保留透明。要透明的帧就选 PNG 或 WebP,别用 JPEG。
#合成:尺寸、帧率与体积
合成 GIF 时最容易犯的错是「自动」尺寸,因为它不缩放 —— 它取所有帧里最大的宽高,各帧居中放入。 随手混进一张超大图,整段 GIF 就被撑到那个尺寸,小图周围全是透明边。
用 动图 GIF 制作器 时,三种输出尺寸的差别很实际:
- 自动:取最大宽高、不缩放、居中放入,尺寸不一致时留透明边
- 按第一帧:以第一帧为准,其余帧缩放
- 自定义:指定宽高,缩放方式可选等比(留边不变形)或拉伸(铺满但变形)
帧率有两个硬数字要记住:帧数至少 2 张、最多 60 张;总像素(宽 × 高 × 帧数)不超过 700 万,超了直接拒绝合成。
延迟单位是 GIF 的 1/100 秒,界面最小 20ms。这里有个绕不过去的下限:浏览器普遍把小于 2(也就是 20ms)的延迟按 100ms 处理,所以把延迟写成 10ms 不会更快,只会得到一段 10fps 的动画。想更流畅只能减帧或降尺寸。
#调色板:色带与闪烁的来源
GIF 的颜色上限是 256 色,而量化会把它压到不超过 255 色(有透明像素时留一个透明槽),所以照片和渐变必然出现色带。 这是格式的天花板,不是压缩参数没调好。
更微妙的是每帧独立量化:相邻帧的调色板不完全一致时,播放中会有轻微闪烁。想要平滑过渡,就用纯色或简单图形做帧;照片类内容改用视频格式(MP4、WebM)体积更小、效果也好得多。
判断一段 GIF 会不会闪烁,看内容里有没有大面积渐变或照片。纯色背景的图标动画、文字动效、线框图这类内容,256 色完全够用,也不会有可见的色带。
#透明:两条相反的处理路线
透明 GIF 有两条相反的处理路线,混用时就会出现白边或黑边。 看到底色不对,先确认原图属于哪一类。
- 用透明做拼图(sprite)的设计:按浏览器实际行为,会保留透明
- 不带透明的普通 GIF:按规范恢复背景色
前者是为了让一个元素叠加到别的背景上,后者是删掉上一帧的残留。同一个 GIF 里混用两种意图,任何工具都很难猜对。
#体积从哪里省
GIF 的体积由「像素数 × 帧数」主导,颜色数的影响相对小 —— 所以减尺寸和减帧永远比调颜色有效。 这也是它比视频格式吃亏的地方。
三条实用的减体积路径:
- 先降尺寸:宽度砍一半,像素数变四分之一
- 再减帧:从 20 帧减到 12 帧,体积按比例下降,观感损失往往可接受
- 考虑换格式:相同内容用 MP4 或 WebM 通常小一个数量级
如果只是要把静态图压小,用 图片压缩 或 图片格式转换 就够了,不用走 GIF 流程。注意动图经过 Canvas 做格式转换只会保留第一帧 —— 部分工具会把 GIF 转成静态图,那是有意的行为,不是 bug。
拆帧会吃很多内存:帧数多或尺寸大的动图,拆到一半卡住通常是内存不够。换个小一点的 GIF,或者分成几段处理,比反复重试有效。解码在本地完成,GIF 不会上传。
#什么时候该用哪个工具
| 你想做的事 | 用哪个 | 关键约束 |
|---|---|---|
| 把多张图合成 GIF | 动图 GIF 制作器 | 2-60 帧,总像素 ≤700 万 |
| 拆开别人的动图看每一帧 | GIF 帧提取器 | 按规范合成,导出 PNG 保透明 |
| 静态图压体积 | 图片压缩 | 缩像素比降质量有效 |
| 换格式看哪种更小 | 图片格式转换 | 动图会只剩第一帧 |
| 把成品图打包成 PDF | 图片转 PDF | 静态图专用,动图只取首帧 |
自制动图的顺序:先用同一尺寸导出所有帧(PNG)→ 合成时按第一帧缩放 → 控制总像素 → 需要透明就保持 PNG 输出。
#常见问题
#合成出来的 GIF 有大片透明边?
因为选了「自动」尺寸,而它不缩放 —— 它取所有帧的最大宽高居中放置,小图周围自然是空白的。改成「按第一帧」或自定义尺寸,各帧才会真正缩放填满。
#帧延迟设成 10ms 为什么没有变快?
因为浏览器把小于 20ms 的延迟按 100ms 处理。这是播放器的实现约定,写在 GIF 文件里也没用。想更快只能减少帧数(每帧内容变化更大)或降低尺寸。
#拆出来的帧缺一块?
说明那个工具没有按规范合成。GIF 允许一帧只存与上一帧的差异,必须按 Disposal Method 逐帧叠加才是完整画面。用 GIF 帧提取器 这类按规范实现的工具就正常。
#渐变背景出现了明显色带?
GIF 只有 256 色,渐变必然被量化出色带。这不是参数问题 —— 想要平滑渐变只能用视频格式,或者把背景改成纯色。
#播放时画面轻微闪烁?
每帧独立量化的结果:相邻帧的调色板不一致,来回切换就看到闪烁。用纯色或简单图形做帧能避免,照片类内容建议直接放弃 GIF。