我拒绝每月花 10 美元买放大器,于是直接在浏览器里做了一个
完整复盘:多步 Canvas 插值的原理、Instagram 九宫格的每格像素账、上线前修过的三次真实移动端崩溃,以及插值放大做不到什么的诚实说明。
促使我做这个功能的,是一张 1600×1600 的裁剪图。我想把它切成 Instagram 3×3 九宫格,账一算很残酷:1600 除以 3,每格只有 533 像素;而 Instagram 方图按 1080 像素展示。也就是说每一格都会被 Instagram 自己的压缩器强行放大——正是那种「我的九宫格怎么发出去就糊了」的效果,这个网站存在的意义就是避免它。
我去找放大工具,试过的每一个不是躲在 9.99 美元/月的付费墙后面,就是让我排在其他用户后面等队列, 要么直接在结果上盖水印。而这件事的本质,不过是一次我的笔记本一秒钟就能完成的重采样运算。
所以我把放大器直接做进了 9grid 的切图工具里。这篇文章是完整的复盘:它怎么工作、上线前在手机上崩了三次都是怎么修的,以及同样重要的——插值放大到底做不到什么。
先算清 Instagram 九宫格的像素账
放大之前先明确目标。9grid 每个切片按 1080 像素或更高导出,所以问题是:你的原图够不够喂饱每一格?
| 布局 | 1080px 切片所需原图 | 1600px 宽的照片实际每格 |
|---|---|---|
| 1×2 轮播 | 2160px 宽 | 800px——需要约 1.4 倍 |
| 1×3 轮播 | 3240px 宽 | 533px——需要约 2 倍 |
| 3×3 主页九宫格 | 3240×3240 | 533px——需要约 2 倍 |
| 3×4 拼图墙 | 3240×4320 | 533px——需要 2 倍以上 |
注意一个规律:几乎所有现实中的「救图」场景都落在 1.5–2 倍之间。这个约束决定了整个设计方向——我优化的是干净的 2 倍放大,而不是英雄主义的 8 倍重建。
它的工作原理:多次小步,而不是一步到位
在 9grid 里点「放大」时,图片全程不离开页面,缩放由浏览器的 Canvas API 完成——但不是一次drawImage 直接画到目标尺寸。一步跳到位的双线性插值会产生肉眼可见的糊边。9grid 的做法是每步最多放大 1.5 倍,反复叠加,直到到达目标。
有两个细节让结果明显好于朴素缩放。第一,每一步都开启浏览器的 imageSmoothingQuality: "high"提示,Chromium 内核会启用更好的重采样核。第二,多次小步插值的叠加效果远比单次大步更接近真正的 Lanczos 滤波——和 Photoshop 时代「每次放大 10%」的老技巧是同一个原理。一次 4 倍放大实际上被拆成五次约 1.32 倍的小跳。
每步之间用 requestAnimationFrame 把控制权交还浏览器,避免两千万像素的画布重绘把页面卡死。另外,如果请求的放大不足 5%,9grid 会直接返回原图——为了不到 5% 的尺寸做一次重采样,纯属白白损伤像素。
上线前在手机上崩溃的三种方式
桌面版第一天就跑通了,然后移动端 Safari 花了一周教我做人。这三个修复至今都留在代码里:
1. iOS 的画布上限
iOS Safari 对约 1670 万像素以上的画布会静默失败——不报错,直接给你一张黑图。手机 1200 万像素照片放大 4 倍远超这个上限。现在 9grid 在移动端把输出钳制在 1200 万像素(桌面 2500 万),并如实告诉你实际生成的尺寸。
2. toDataURL 冻屏
最早的导出用的是 canvas.toDataURL()——一个在主线程上同步拼 base64 字符串的调用。大画布下这个字符串有几十 MB,中端手机直接冻死。换成异步的 toBlob()后彻底解决。
3. 被垃圾回收拖垮
每个 1.5 倍小步都新建一个画布,几步下来给垃圾回收器堆了几百 MB。现在改为两个画布交替充当源和目标,无论放大要跳多少步,内存都保持平稳。
插值放大老实说做不到什么
这是大多数放大器落地页不会写的部分。Canvas 插值只是重新分配你已有的像素,不会凭空发明你没拍到的细节。一张 500px 的人脸放大 4 倍,得到的是一张平滑、无瑕疵的 2000px 人脸——但不会变锐利。想要「无中生有」的细节(真正的 "enhance"),需要机器学习模型:要么下载几十 MB 的模型文件(刚好会压垮我前面救回来的那批中端手机),要么走服务器端处理(隐私和成本的代价又回来了)。
对 Instagram 九宫格这个场景来说,这笔账几乎总是偏向插值:
- 你的缺口很小。上面表格已经说明,真实的九宫格工作流需要的是 1.4–2 倍——在这个区间,高质量插值和 ML 模型在照片上的差距几乎看不出来。
- Instagram 反正要再压一遍。模型幻想出来的精细纹理正是它的压缩器最先抹掉的东西;能活下来的是干净的边缘和「没有放大糊」——这恰恰是插值给你的。
- 随处可用、即点即得。不下载模型、不排队、不上传,照片全程留在你浏览器的内存里。
我的经验法则:原图达到目标尺寸的一半以上,就放心本地放大然后发;想把一张 300px 的缩略图变成海报,任何免费工具——包括我这个——都做不到,别信说能做到的。
完整工作流
把照片传进切图工具,选好布局,看尺寸提示:如果有切片会低于 1080px,9grid 会推荐放大倍数(缺口小推 2 倍,缺口大推 4 倍)。点「放大」,等进度跑完,再切图。整个弯路在普通笔记本上大约多花五秒。