纯Vibe零代码:一个纯前端证件照修改器的设计与实现

纯Vibe零代码:一个纯前端证件照修改器的设计与实现

发表于 2026/05/29 2148 字 8 分钟
AI 摘要 由 AI 自动生成

|

这篇文章记录的是「证件照修改器」的设计与实现过程。

线上版本已经发布:https://idphoto.hex.ac.cn/。纯 Vibe 零代码,欢迎体验。

证件照修改器介绍

它的核心原则很简单:图片只在用户浏览器中处理,不上传服务器;应用支持离线使用。

为什么做成纯前端 PWA

证件照工具天然涉及个人照片,隐私敏感度很高。如果后端参与处理,哪怕服务端不保存图片,用户仍然需要信任传输链路、服务器实现和日志策略。对一个只想快速改尺寸、换底色、压缩文件大小的用户来说,这种信任成本其实偏高。

另一方面,很多原生 App 或在线服务会把“换底色”“高清导出”“无水印保存”放到付费墙后面。工具本身并不复杂,但用户需要为一次临时处理安装 App、授权相册、注册账号,甚至充值会员。这和“临时、快速、私密地处理一张证件照”的需求并不匹配。

所以这个项目选择纯前端路线:

  • 图片读取、裁剪、压缩、格式转换全部在浏览器中完成。
  • 人像抠图使用浏览器端 MediaPipe 模型推理。
  • PWA 缓存应用壳、WASM、模型文件,首次加载完成后可以离线继续使用。
  • 没有后端 API,没有图片上传,也不需要账号。

这让工具的使用边界更清楚:用户打开页面后,浏览器就是完整运行环境。

产品能力

当前版本主要支持:

  • 上传 JPG / PNG 图片。
  • 生成 1 寸、2 寸或自定义尺寸图片。
  • 支持像素尺寸,也支持毫米 + DPI 换算。
  • 手动拖拽、缩放裁剪。
  • 智能人像抠图换背景。
  • 支持白、蓝、红、灰、自定义背景色。
  • 支持透明背景 PNG。
  • 支持 JPEG / PNG 导出。
  • JPEG 可按最大 KB 做质量搜索压缩。
  • 可离线使用。
  • 支持简体中文、繁体中文、英语。

使用入口被设计在裁剪区域本身:用户打开页面后,点击中间的「上传图片」区域即可开始。

整体架构

项目采用 Vite + React + TypeScript,核心逻辑基本都集中在浏览器端。

flowchart LR
  User["用户选择图片"] --> Browser["浏览器本地运行"]
  Browser --> Canvas["Canvas 裁剪 / 重采样"]
  Browser --> Segmenter["MediaPipe 人像分割"]
  Segmenter --> Mask["生成前景 Mask"]
  Canvas --> Compose["背景合成"]
  Mask --> Compose
  Compose --> Compress["JPEG 压缩 / PNG 输出"]
  Compress --> Download["本地下载图片"]

  SW["Service Worker"] --> AppCache["App Cache"]
  SW --> ModelCache["Model Cache"]
  AppCache --> Browser
  ModelCache --> Segmenter

浏览器端分成几层:

  • UI 层:React 管理尺寸、格式、背景模式、压缩参数、语言切换。
  • 图像处理层:Canvas 负责裁剪、缩放、背景合成和导出。
  • 人像分割层:MediaPipe Tasks Vision 在浏览器中加载 WASM 和模型文件。
  • PWA 层:Service Worker 管理 App Shell 和模型资源缓存。
  • 恢复层:如果旧 Service Worker 导致资源 MIME 异常,HTML 内联脚本会清理旧缓存并恢复页面。

图像处理流程

图片上传后,应用先用 createImageBitmap 读取图片,避免把图片发到任何服务器。裁剪区本质上是一个固定比例的视口,用户拖动和缩放时只更新偏移量和缩放比例。

导出时会把用户看到的裁剪区域映射回原图坐标:

  1. 根据目标尺寸比例确定裁剪框。
  2. 根据当前缩放、偏移计算原图采样区域。
  3. 使用 Canvas drawImage 重采样到目标宽高。
  4. 如果启用换背景,先生成前景图层,再用人像 mask 做 destination-in 合成。
  5. 根据目标格式输出 PNG 或 JPEG。

JPEG 压缩使用二分搜索质量参数,尽量在不改变尺寸的情况下压到指定 KB 以下。PNG 是无损编码,浏览器原生能力有限,因此如果 PNG 超过目标大小,应用会明确提示,而不是偷偷改变用户指定的尺寸。

智能换背景

换背景是这个工具里最有挑战的部分。它不是简单取某个颜色做透明,而是使用浏览器端人像分割模型。

实现上使用 @mediapipe/tasks-vision

  • WASM 文件来自 npm 包。
  • 人像分割模型放在 public/mediapipe/models
  • 首次使用时浏览器加载模型。
  • 同一张图片会复用 mask,切换背景色或输出尺寸时不重复推理。

为了减少边缘残留,mask 会做几步处理:

  • 优先选择 person / foreground 通道。
  • 用阈值把低置信度背景剔除。
  • 根据「边缘净化」强度做轻微收缩。
  • 再做羽化,避免边缘过硬。

这个方案不需要后端,也不依赖云端 AI 服务。缺点是模型大小和首次缓存成本都在浏览器端承担,所以 PWA 缓存设计很重要。

PWA 缓存设计

最初版本把 App Shell、JS/CSS、WASM、模型都放进一个缓存里。这样可以工作,但有两个问题:

  1. 每次 UI 发版都会导致模型资源跟着重新缓存。
  2. 旧版本 Service Worker 如果处理不当,可能把 HTML 兜底响应返回给 JS 模块,触发 MIME 错误。

现在缓存被拆成两组:

  • id-photo-app-v10:HTML、JS、CSS、manifest、icon。
  • id-photo-model-v1:MediaPipe WASM 和模型文件。

这样 UI 发版只更新 app cache;模型不变时不会反复下载 30MB 级别资源。

Service Worker 对不同请求采用不同策略:

  • 页面导航:network-first,失败后返回缓存的 index.html
  • JS / CSS / WASM:cache-first,但必须校验 MIME 类型。
  • MediaPipe 模型资源:独立模型缓存,避免被 UI 发版清理。

同时启用了 navigationPreload,让页面导航请求和 Service Worker 启动并行,降低冷启动时的等待。

解决白屏和旧缓存问题

PWA 项目很容易遇到一个经典问题:发布新版本后,旧的 index.html 还引用旧 hash 的 JS 文件,而服务器已经只有新的 hash 文件。某些开发服务器或 fallback 规则会把不存在的 JS 请求返回成 index.html,浏览器就会报:

Expected a JavaScript-or-Wasm module script but the server responded with a MIME type of "text/html"

这个项目做了两层保护:

  • Service Worker 不会再把 HTML 返回给 JS / CSS / WASM 请求。
  • HTML 内联恢复脚本会监听 module script 加载失败,自动注销旧 Service Worker、删除旧缓存,并刷新到恢复地址。

这样用户不需要打开 DevTools 清缓存,应用可以自我恢复。

首屏与一屏布局

作为 PWA,它更像一个工具应用,而不是普通网页。因此布局目标是:桌面端尽量一屏完成主要操作。

当前布局采用:

  • Header:LOGO、名称、描述、语言切换。
  • 左侧:尺寸、单位、格式、背景、压缩参数。
  • 中间:上传 / 裁剪区域。
  • 右侧:输出预览和下载。
  • Footer:技术支持和源码入口。

桌面端固定 100vh,页面整体不滚动,左右面板内部滚动。裁剪区高度受容器限制,不再跟随浏览器高度无限变大。移动端则保留自然滚动,保证小屏可操作。

构建与资源同步

MediaPipe 的 WASM 文件来自 npm 包,而模型文件放在 public 目录。为了避免手动复制资源导致 CI/CD 漏文件,项目增加了构建前资源同步脚本:

npm run sync:assets

构建命令会自动执行它:

npm run build

脚本会做两件事:

  • node_modules/@mediapipe/tasks-vision/wasm 同步 WASM 文件到 public/mediapipe/wasm
  • 检查 selfie_segmenter.tflite 模型是否存在且大小合理。

这样换机器、CI/CD 构建、重新安装依赖后都能更稳。

如何使用

线上地址:

https://idphoto.hex.ac.cn/

使用步骤:

  1. 打开页面。
  2. 点击中间「上传图片」区域。
  3. 选择 1 寸、2 寸或自定义尺寸。
  4. 必要时切换像素 / 毫米 + DPI。
  5. 拖动照片调整裁剪位置,使用缩放滑杆调整主体大小。
  6. 选择背景:原背景、智能换色或透明。
  7. 设置输出格式和最大 KB。
  8. 点击「生成图片」。
  9. 检查预览后下载。

所有处理都在浏览器中完成。首次打开时应用会缓存离线资源,完成后即使断网也可以继续使用。

小结

这个证件照修改器表面上是一个图片工具,底层其实是一个完整的浏览器端应用实践:

  • 用 Canvas 完成图像处理。
  • 用 MediaPipe 在浏览器中做人像分割。
  • 用 PWA 让工具离线可用。
  • 用缓存拆分降低大模型资源的重复下载。
  • 用 MIME 校验和恢复脚本处理 PWA 常见白屏问题。
  • 用一屏式布局让它更像工具,而不是网页。

对于隐私敏感、小而完整的工具型应用,纯前端 PWA 是一个很值得尝试的方向。


该文章由 ChatGPT 5.5 生成,人工做了细微调整。

作者: 小谈谈
声明: 本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。