01 / 背景:从一份动效素材的交付问题开始
最初,我想解决的事情很简单:手里有一份 SVGA 动画,需要把它转换为 PAG。
这类需求看起来只是更换文件后缀,实际牵涉到不同格式的渲染与编码方式。画面是否清晰、透明背景能否保留、尺寸和帧率是否正确,以及生成文件能否被目标播放器正常读取,都需要逐项确认。
我希望把这些处理过程封装起来,让使用者添加素材、调整必要设置,就能得到可交付的结果。素材在电脑上处理,也能省去上传和下载的往返。
因此,项目最早的目标是:先跑通一条真实的转换链路,再把它做成一个可重复使用的工具。
02 / 从 0 到 1:先有可用链路,再形成产品
第一步,是验证 SVGA 到 PAG 的转换方式:通过本地播放器逐帧渲染动画,得到带透明通道的画面,再进行 PAG 编码。由此保留尺寸、帧率和透明背景,但原始图层会合并,输出也存在有损压缩。
最早的版本采用脚本和浏览器页面完成操作。它已经具备文件添加、批量处理、转换状态和输出位置等基础能力,但使用方式仍然带着工具脚本的痕迹。
在实际使用后,我提出了一个明确的要求:
「做成一个像是电脑 app 的那种。」
这次反馈改变了产品的方向。接下来需要解决的,是启动方式、预览、文件管理和重复导出等完整体验。用户应该能看懂当前状态,并知道下一步该做什么。
03 / 产品功能:把相邻的素材处理需求接进同一条流程
随着使用场景增加,Frame 逐步接入 GIF 导出、PNG 序列、MP3 压缩和图片压缩。新增功能围绕同一个目标展开:减少素材交付前反复切换工具、确认参数和查找结果的操作。
截至 3.4,Frame 包含 8 项工具:
| 工具 | 解决的任务 | 主要设置与边界 |
|---|---|---|
| SVGA → PAG | 转换动效交付格式 | 小体积—均衡;保留原尺寸、帧率与透明背景,图层合并 |
| SVGA → GIF | 导出便于查看和分享的动画 | 尺寸、帧率、背景、调色板质量 |
| PAG → GIF | 将 PAG 画面转换为通用动画 | 与 GIF 导出采用一致的设置逻辑 |
| SVGA → PNG 序列 | 提取透明画面,供后续制作使用 | 原始全帧或指定帧率、尺寸、连续编号 |
| PAG → PNG 序列 | 逐帧导出 PAG 的实际渲染画面 | 保留透明背景,支持尺寸和提取帧率调整 |
| MP4 → PNG 序列 | 从视频中提取连续画面 | 提取帧率与输出尺寸;PNG 编码无损 |
| MP3 压缩 | 在文件大小和听感之间做选择 | 可调音质;最高档保留原音频流,体积可能不变 |
| 图片压缩 | 优化静态 JPG、PNG、WebP | JPEG / WebP 画质、长边尺寸、格式选择及原图对比 |
多种能力共用一条操作路径:选择工具 → 添加文件 → 查看预览 → 调整设置 → 导出 → 检查结果。
文件可以拖入,也可以多选添加;队列支持搜索和状态筛选。转换过程中可以停止任务,完成后可以打开结果位置。调整设置后,能够重新导出;原结果保留,同名文件自动编号。
04 / 设计:让一个工具具有桌面应用的使用秩序
围绕任务组织信息
桌面化之后,界面形成了稳定的工作区:侧栏负责切换工具,文件队列负责管理任务,中央区域负责查看画面,右侧负责文件信息与导出设置。
这个布局把「处理哪个文件」「画面是什么样」「要怎样输出」放在相邻的位置。功能扩展时,用户可以沿用已经理解的操作方式。
视觉上,界面采用浅色底、清晰的分区和紫色操作强调。大部分区域保持克制,把注意力留给素材预览、状态变化和主要操作。
空状态也要说明当前任务
早期不同工具沿用了同一张 SVGA → PAG 配图。切换到 GIF 或音频压缩后,画面表达与功能不符。我据此提出:
「空状态的配图要根据选项的功能来。」
随后,每种工具都有了对应的输入输出图示、说明文字和选文件按钮。SVGA → GIF 展示格式变化,PAG → PNG 展示帧序列,图片压缩展示原图与压缩后,MP3 则使用音频语义。
用户还没有添加文件时,页面就已经开始解释这个工具的作用。
为 Frame 建立独立的视觉标识
Logo 探索了三种简洁、具有科技感的方向,最终选择 C 方案。正式图标以折叠的金属 F 为主体,搭配黑色圆角底,强调字母识别和空间层次。
图标进一步整理为可用于应用的 PNG 与 macOS 图标资源,让项目从页面标题延伸到桌面图标和应用身份。
完成后,仍然可以继续调整
第一次导出后,设置仍然可以修改。界面会标明「设置已调整,上次结果保持不变」,用户再按当前设置导出新文件。结果对比、重新选择参数和保留旧文件因此连成一条流程。
05 / 一个关键取舍:为什么去掉 PAG 的「高质量」档
在转换能力稳定后,我注意到部分 PAG 输出不够清晰,于是提出加入质量选择,并允许完成后调整设置重新导出。
早期尝试了低、中、高三个档位。但用同一份 SVGA 对比输出后,我发现:文件体积差距很大,肉眼感受到的画面改善却不明显。
「如果差异并不显著我建议去掉【高】选项。」
检查实现后,问题逐渐明确:当时不同档位使用了不同的存储策略。压缩视频可以利用相邻画面的重复信息;逐帧位图方案则会保存更多独立的画面数据。即使都在播放相同尺寸、相同时长的动画,体积也可能相差很大。
编码方式带来的数据增加,不一定会转化为同等明显的视觉收益。原素材已有的细节上限,也无法通过提高输出档位补回来。
因此,后续版本将 PAG 输出统一到视频压缩路径,移除了体积代价过大的逐帧无损「高」档,保留「小体积—均衡」的选择范围。界面同时明确:滑块表达编码取舍,改善幅度取决于素材。
这次迭代也影响了其他功能的参数设计。PNG 使用无损编码,无需套用统一的画质滑块;GIF 受调色板与透明能力限制;JPEG / WebP 的质量档位,也不应解释为原图保真率。
我更关心的是:每一个选项是否对应用户能理解、也值得承担的成本与收益。
06 / 技术实现:把渲染、编码与文件交付连起来
Frame 的界面之下,是一条本地媒体处理链路:读取文件并检查基本信息,调用对应播放器或解码器,生成画面,再编码并写入输出文件。不同格式使用适合自己的路径。
| 层次 | 实现方式 |
|---|---|
| macOS 宿主 | Swift / AppKit,结合 WebKit 承载工作台与本地渲染;音视频相关能力使用系统框架 |
| Windows 宿主 | Electron + Node,处理桌面交互、文件任务和转换流程 |
| 动画渲染 | SVGAPlayer-Web / libpag,在本地还原输入素材的画面 |
| PAG 编码 | macOS 使用 PAG 转换组件及编码桥接;Windows 实现便携的视频型 PAG 写入路径 |
| 媒体与图片 | FFmpeg 处理音视频任务;图片使用浏览器编码能力与本地 WebAssembly 编解码器 |
| 输出交付 | 状态反馈、取消、同名编号、设置变更识别与重新导出 |
为了在 Windows 上保留相近的产品流程,需要重新适配宿主和编码链路。透明 PAG 输出还需要处理颜色与 alpha 信息,并通过播放器解码结果确认它们能被正确还原。
这些细节决定了应用生成的文件能否真正进入下一步制作流程。
代码细节一:压缩后更大,就保留原图
图片压缩不能只以「编码成功」作为完成标准。对已经优化过的图片,再编码可能让文件变大。
Frame 在同格式、同尺寸的情况下判断实际输出大小。如果没有得到更小的结果,就保留原始图片,并说明原因。
// 来自图片处理实现,展开排版以便阅读。
const keepOriginal =
format === info.imageFormat &&
factor === 1 &&
encoded.length >= bytes.length;这让「压缩」的行为与用户预期保持一致,也避免把更大的文件包装成优化结果。
代码细节二:屏幕上看着正常,还要检查实际像素
PNG 序列导出曾遇到 Retina 环境中的尺寸问题:屏幕缩放比例可能进入渲染过程,造成导出像素尺寸偏离原始设置。后续在 PAG 渲染视图中显式关闭自动缩放,并检查实际输出尺寸。
图片无损优化也采用类似的验收方式。针对 PNG 原格式、不缩放的优化路径,测试会解码输出文件,并逐像素比较 RGBA 数据:
// 来自图片压缩集成测试,变量名沿用源码。
assert.equal(decoded.width, png.width);
assert.equal(decoded.height, png.height);
assert.deepEqual(decoded.data, png.data);视觉检查与数据检查分别回答不同的问题:前者判断效果是否合适,后者确认尺寸、透明和像素等承诺是否成立。
07 / Vibe Coding 的工作方式:把反馈变成下一版的验收条件
Frame 的需求是在使用中逐步明确的。每次得到可运行版本后,我会检查结果、提出具体问题,再让 AI 把这些判断转成界面或代码上的修改。
| 实际反馈 | 对应的产品或工程调整 |
|---|---|
| 使用起来像脚本,希望像电脑 App | 转向桌面工作台,补齐预览、队列与导出管理 |
| 想在导出前选择质量,完成后还能再调整 | 增加质量设置、设置变化提示和重新导出 |
| 高档体积增加很多,画面改善有限 | 检查编码策略,收窄 PAG 质量范围 |
| 不同工具都显示同一张空状态配图 | 为每种工具匹配图示、说明和操作入口 |
| 希望支持图片压缩 | 扩展现有工作流,并验证透明、方向、尺寸与原图保留 |
在这个过程中,我负责说明需求、判断结果、决定取舍;AI 负责提出实现方案、编写和调整代码,并协助完成验证与打包。
有效的协作往往来自具体的描述。例如,「画质不好」需要进一步变成同一素材的结果对比;「不好用」需要落到缺少预览、状态不清晰或导出后不能继续调整等问题上。
每一次反馈,都在帮助下一版形成更明确的验收条件。
08 / 交付:让项目能被下载、理解和继续维护
Frame 后续完成了公开仓库、版本发布、macOS 应用包、Windows 安装器与便携包,以及使用、构建和第三方组件说明。
版本迭代也逐步变得可追溯:3.2 整理跨平台开源交付;3.3 增加 SVGA / PAG 的 PNG 序列导出并完善空状态;3.4 增加图片压缩及相应验证。
验证覆盖实际转换、透明通道、尺寸与帧率、取消、重复导出和同名不覆盖等路径。图片压缩还检查了 PNG 像素一致性、WebP 透明、JPEG 方向,以及动态或损坏图片的拒绝处理。
当前交付也保留明确边界:Windows 构建产物已完成静态检查,共享实现已在 macOS 的 Electron 环境中验证;Windows 真机的安装、运行与卸载仍待验证。应用尚未进行商业代码签名,macOS 包未公证。
Frame 自有源码采用 MIT,播放器、编码器等第三方组件保留各自许可。
09 / 回看这次实践
这次项目让我看到,AI 协作可以把需求转化为可运行的版本,也让持续试用和快速调整变得更容易。与此同时,产品仍然需要有人判断:结果是否可接受、参数是否有意义、功能是否容易理解,以及完成到什么程度才算可以交付。
Frame 最有代表性的进展,是这些判断逐渐进入了产品:空状态解释当前任务,质量选项承认格式的限制,重新导出保留旧结果,图片压缩在没有收益时保留原图。
从一个 SVGA 转换问题出发,最终留下了 8 项工具、一套连贯的桌面操作流程,以及可以查看源码、下载版本和继续迭代的项目。
提出真实问题,交付可用版本,通过实际结果做下一次判断。 这是我在 Frame 中形成的 Vibe Coding 工作方式。