图片压缩器读取一张本地图片文件, 在画布上把它重编码成 JPEG, 在下载之前就显示大小变化. 用文件选择器选中文件后, 页面按原始尺寸把它画到画布上, 画布再按滑块上的质量重编码, 默认是 70%. 读数随后打印原始大小, 估算后的压缩大小和百分比, 例如 2,400,000B → 833,351B (-65%), 并排预览把原图和结果放在一起. 一切都是浏览器内的画布调用: 文件字节不离开设备, 全程没有上传.
工具接收什么, 给出什么
输入是单个只接受图片文件的文件字段, 不需要任何其他东西. 原始大小是文件在磁盘上的精确字节数, 所以读数左半边是准的. 输出永远是 JPEG: 画布把像素交给浏览器的 JPEG 编码器, 输出格式固定不可选, 宽高保持输入原样. 唯一的控件是质量滑块, 从 10 到 95, 步长 5, 共 18 档, 拖动它会立即用保留的原图重新编码, 读数和预览实时刷新. 满意之后, 下载按钮把结果写成本地磁盘上的 compressed.jpg; 你保存的文件才是实际字节数的准绳.
为什么输出永远是 JPEG
重编码是一次画布调用, 输出类型被硬性设为 JPEG, 所以 PNG 进去 JPEG 出来, WebP 进去 JPEG 出来, GIF 进去只保留第一帧, 因为图片元素只解码动画文件的第一帧. 固定格式带来两个后果. 第一, JPEG 没有 alpha 通道, 所以透明的 PNG 不会保持透明: 透明像素在编码前被合成到黑色上, 你下载到的文件在原图透明的地方是黑色. 第二, 工具用的是浏览器自己的解码器, 没有后备, 所以浏览器解不开的格式, 比如大多数平台上的 HEIC, 干脆永远解不完, 页面对它什么都不显示.
大小是怎么读出来的
压缩大小是一个估算值, 而且是刻意简单的估算. 画布返回一个 data URL, 由 23 个字符的 data:image/jpeg;base64, 前缀加 Base64 组成, 页面取整个字符串长度乘以四分之三, 因为 Base64 每 3 字节用 4 个字符. 这个估算只因前缀就比真实字节数多出 17 到 19 字节: 1,000,000 字节的 JPEG 读作 1,000,019B, 999,999 字节的读作 1,000,016B. 完整跑一遍, 一个 2,400,000B 的文件, 编码器输出 833,333 字节, 打印出 2,400,000B → 833,351B (-65%); 百分比取整, 所以显示的数字和精确算术可能差 1 个百分点.
文件变大时
节省百分比是 (1 减去压缩大小除以原始大小) 乘以 100 的取整, 负数显示为加号, 所以变大的文件报出来例如 45,000B → 47,018B (+4%). 这发生在小图上, 编码器固定开销占主导; 也发生在原图本就是 JPEG 且已压到不高于所选质量时; 还发生在滑块高于原图实际保存质量时. 方向不是 bug: 10 是输出最小的一档, 95 是最大的一档, 如果调高质量时读数往上走, 说明原图已经比编码器想做的更精简.
像素去了哪里
文件用 FileReader 读成页面上的 data URL, 唯一的计算就是画布加 JPEG 编码, 所以字节不离开设备, 全程没有任何网络请求. 也没有持久化: 页面只在打开期间保留 data URL, 没有历史, 没有缓存, 关掉或刷新标签页, 图片和结果就都丢弃. 唯一能离开设备的产物是你选择下载的 compressed.jpg, 而且是你自己选的.
它不是什么
工具保持输入尺寸原样, 所以它不是尺寸调整工具; 改像素尺寸是 图片尺寸调整 的活. 它也不是格式转换器, 因为输出固定为 JPEG; 选输出格式是 图片格式转换 的活. 它不读取也不显示文件的元数据, 那些由 图片信息查看器 展示; 它也不裁剪画面, 裁剪看 裁剪指南.