我用 Vue3 + Web Workers 做了一个胶片效果模拟器,全程在浏览器运行

0 阅读2分钟

before-after

在线体验:seamys.github.io/grainlab/ GitHub:github.com/seamys/grai…


为什么做这个

我喜欢胶片摄影,但胶片的感觉很难用手机照片还原——要么买昂贵的 Lightroom 预设, 要么用某些 App 加水印、逼你订阅。

所以我做了 GrainLab,免费、开源、不上传任何照片,直接在浏览器里跑。


功能介绍

  • 🎞 胶片颗粒:基于亮度分布的颗粒,而不是简单的随机噪声
  • 🌅 光晕(Halation):模拟胶片高光区域的红橙色渗透效果
  • 💡 漏光(Light Leak):随机光斑效果
  • 🔲 暗角(Vignette)
  • 📈 色调曲线:RGB 独立曲线调整
  • 🎨 色彩分级(Color Grading)
  • 20+ 胶片预设:Kodak Portra、Fuji 400H、Ilford HP5 等风格

技术实现

技术栈

  • Vue 3 + Composition API
  • TypeScript
  • Vite
  • Canvas API
  • Web Workers
  • Pinia(状态管理)

核心:Web Worker 图像处理管线

所有滤镜运行在独立的 Worker 线程里,主线程不阻塞。 每个滤镜是一个纯函数 (ImageData, params) => ImageData,可以任意组合、调整顺序。

// 管线示例
const pipeline = [
  applyToneCurve,
  applyColorGrade,
  applyGrain,
  applyHalation,
  applyVignette,
]
result = pipeline.reduce((img, fn) => fn(img, params), source)

胶片颗粒算法

真实胶片的颗粒分布和画面亮度相关——暗部和高光的颗粒结构不同。 我通过对每个像素的亮度值进行加权采样来模拟这一特性:

const luminance = (r * 0.299 + g * 0.587 + b * 0.114) / 255
const grainAmount = params.intensity * (1 - Math.abs(luminance - 0.5) * 0.8)

Halation(胶片光晕)实现

Halation 是光线穿透胶片乳剂层后在背面反射产生的红橙色光晕。 实现方式:提取高光区域 → 高斯模糊 → 染色(偏红橙)→ 叠加回原图。


部署

纯静态站,托管在 GitHub Pages,零运维成本。


后续计划

  • 移动端手势优化
  • 更多胶片预设
  • 自定义预设保存与分享

欢迎 Star ⭐ 和提 Issue,有任何胶片效果的建议都可以留言!

GitHub:github.com/seamys/grai…


发帖设置

  • 标签Vue.js 前端 开源 Canvas 工具
  • 封面图docs/images/social-preview.png
  • 发布时间:工作日上午 10 点或下午 3 点