Skip to content

Latest commit

 

History

History
166 lines (122 loc) · 9.86 KB

README.md

File metadata and controls

166 lines (122 loc) · 9.86 KB

Brushtrokes · 笔触工坊

把任意图片重绘成「一笔一笔画出来」的过程动画,内置 八种风格:油画、线描、色块、素描、水墨、点彩、霓虹、像素。 纯前端实现,图片不上传、无需后端,可直接托管到 GitHub Pages。

仓库:https://github.com/tyuecode/tyuecode.github.io

在线体验:https://tyuecode.github.io/

八种风格

风格 工序 效果
油画 刷底色 → 起稿 → 单色底层 → 铺大色 → 塑造 → 塑形 → 罩染 → 干扫 → 细部 → 提白 十道古典工序,起收锋、笔毛分叉、颜料渐枯、厚涂浮雕、罩染透叠
线描 定形 → 勾主线 → 结构线 → 细描 → 点睛 近黑底上的细白线,只勾结构,像版画稿
色块 铺底色 → 暗部色块 → 中间调 → 亮部 → 点缀 分层平涂的硬边色块,限量配色的海报感
素描 起形 → 铺调子 → 排线 → 交叉线 → 提亮 纸底石墨排线,顺结构走向,暗部交叉线压深
水墨 铺水 → 勾线 → 渲染 → 皴擦 → 点苔 → 留白 宣纸墨色,浓淡枯湿靠叠墨,留白透气
点彩 底色 → 中层 → 密集 → 边界 → 高光 密集小色点堆成画面,颜色靠视觉混色
霓虹 铺底 → 主线 → 细线 → 光晕 → 亮点 深底发光线条,像通电的招牌
像素 低分辨率 → 中分辨率 → 细化 → 高光格 量化成方格逐格点亮,复古游戏感

每种风格都有自己的一套工序、笔触语言和底纹,不是同一套笔刷换配色: 线描的线贴着 Canny 脊线吸附;色块把分层区域抽成轮廓直接填充;素描按明暗分四遍排线; 水墨用叠墨做浓淡、把纹理压到最疏;点彩是六边形错排撒点、暗部点更大以免白纸透出; 霓虹用加色混合做辉光但主体走正常混合(否则密集处会叠成白);像素先铺大格再补细格。

特点

  • 🎨 八种风格:油画 / 线描 / 色块 / 素描 / 水墨 / 点彩 / 霓虹 / 像素,各有一套工序和笔触语言
  • 🖌 物理笔触:起收锋、笔毛分叉、颜料渐枯、厚涂浮雕、罩染透叠
  • 🔒 本地处理:图片只在你的浏览器里,绝不联网上传
  • 📦 可下载:播放器打包成单个 HTML 文件,双击离线播放、随意分享
  • 🖼 高清终帧:终帧按 1× / 2× / 3× 离屏重绘,不受屏幕尺寸限制
  • 📱 响应式:桌面双栏、移动端单栏,自动适配触屏
  • ⚡ 快:1400px 图约 1.5–3 秒,数万条笔触

使用

  1. 打开 https://tyuecode.github.io/(或本地 python -m http.server 后访问)
  2. 拖入 / 选择图片(PNG / JPG / WEBP)
  3. 选一种风格
  4. 调「细腻程度」「动画时长」「处理精度」
  5. 点「开始生成」
  6. 页面内实时预览,或下载单文件播放器 / 导出终帧 PNG

「细腻程度」在每种风格下的含义不同,界面上会跟着所选风格给出说明:

风格 往左(更细) 往右(更粗)
油画 / 线描 / 素描 笔触更细更密,细节更清楚 笔触更粗犷,只留主干
色块 保留更多小色块,边缘贴原图 小色块被并掉,画面更概括
水墨 墨色更细碎 笔触更宽,更像大笔挥洒
点彩 点更密更小,接近照片 点更大更疏,更像修拉
霓虹 线条更多更密 只留最亮的主干
像素 格子更多更清晰 格子更大更复古

导出终帧时可以选 1×(原图尺寸)、2×、3×。终帧是在屏幕外的独立画布上 按所选倍数重新绘制的,不受预览窗口大小限制:

档位 输出尺寸(以 1527×1000 为例) 大致文件大小
原图尺寸 1527×1000 ~3.5 MB
2 倍 3054×2000 ~13 MB
3 倍 4581×3000 ~27 MB

文件结构

文件 说明
index.html 主页面(上传、选风格、调参、实时预览、下载)
core.js 分析 + 八种风格的笔触生成 + 时间轴
render.js 共用的笔触渲染器(网页预览与播放器共用,保证所见即所得)
worker.js Web Worker,后台生成不卡界面
player-template.html 下载用的单文件播放器模板(内联 render.js)
build.js 资源版本号(见下方「更新后请改版本号」)
.gitattributes 统一用 LF,避免 Windows 上自动转 CRLF 产生无意义 diff
.github/workflows/deploy.yml GitHub Pages 自动部署

更新后请改版本号

GitHub Pages 给静态资源发的是 Cache-Control: max-age=14400(4 小时)强缓存, 而且 URL 里没有文件指纹。所以改了 core.js / render.js / worker.js 之后, 老访客会在最长 4 小时内继续用旧脚本,表现就是「新页面 + 旧 worker」: 选了线描却还是出油画、风格一栏显示 undefined。

为此 index.html 里所有脚本引用都带 ?v= 查询串,由 build.js 的 OIL_BUILD 统一提供:

window.OIL_BUILD = '20261111-brushtrokes';

每次改动这些脚本,就把 OIL_BUILD 换一个新值(如日期或递增序号), 浏览器会把它当成全新 URL 去取。worker.js 也会把这个查询串透传给 importScripts('core.js'), 保证 worker 和 core 版本一致。

生成完成时页面会校验 result.style 是否等于用户选的风格——旧版 worker 不认识 style 字段,不匹配就明确报错提示强制刷新,不会静默输出一张油画糊弄过去。

技术实现

分析一次,八种风格共用。 先在降采样后的工作分辨率上算一张「结构图」:

  1. 分层:在 L*(亮度)× S(饱和度)平面上做 k-means(k-means++ 初始化),把画面按材质和明暗分开
  2. 边缘:两套 Canny(粗 σ=3.0 出主要轮廓,细 σ=1.2 出细节边缘),双阈值 + 滞后连接
  3. 方向场:结构张量(Sobel → 特征分解)求每个像素的主导走向;平坦区改用低频方向场,避免笔触乱指
  4. 显著性:梯度幅值 + 方向一致性 + 细边缘密度加权,告诉笔触「哪里值得多画几笔」

八种风格从同一张结构图出发,各取所需:

  • 油画:网格播种 → 沿方向场行走 → 按显著性分配笔宽和密度,十道工序逐层加细
  • 线描:种子只撒在 Canny 脊线上,行走时向法线方向吸附(requireEdge),走偏即止;用 maxTurn 限制转角,防止笔画打卷成毛线团
  • 色块:对每个分层跑 marching squares 抽闭合轮廓,用 RDP 简化(闭合轮廓要在离起点最远处切成两条开链,否则首尾重合会让 RDP 退化),最后用 evenodd 填充规则一次平涂
  • 素描:按局部亮度分四遍排线,rot90 让交叉线垂直正交;石墨色按原图明暗映射,亮部再用高亮笔擦回纸白
  • 水墨:墨色不用纯黑而带暖调,浓淡靠同一色相不同透明度叠出来;笔触宽而短像墨团化开,皴擦刻意压到最疏
  • 点彩:六边形错排撒点,每个点在原色附近做色相抖动靠眼睛混色;暗部点自动放大,因为纸是白的,暗处点太小会从缝里透出白点像噪点
  • 霓虹:lighter 加色混合做辉光,但主体走正常混合——密集区域纯靠加色会亮度累加到饱和成白纸;亮处目标亮度用正弦收一点,避免高光区糊成一片
  • 像素:先铺大格再补细格,每通道量化到 5 档做调色板感;格边长由「细腻程度」控制

笔触类型:render.js 里定义了 8 种笔触形态(NORMAL 普通、GLAZE 罩染、 SCUMBLE 干扫、IMPASTO 厚涂、FILL 闭合填充、DOT 圆点、GLOW 发光、PIXEL 方块), 各风格按需选用。

严格串行的时间轴:start[i] == start[i-1] + dur[i-1]。每道工序按权重分配时长, 工序内按 0.0035 + 0.0010×弧长 + 0.0006×笔宽(色块额外按面积开方)分配每笔时长,然后逐条校验零重叠零断点。

颜色取自原图:沿笔触取 5 个采样点的中位色,只做轻微抖动,绝不用调色板。 色块风格例外——它按设计就是要用量化后的限量配色。

网页与播放器共用渲染器:render.js 被 index.html 和下载的单文件播放器共同引用, 所以页面里看到的效果和下载后播放的完全一致。

笔触保底机制:大笔触风格(油画 / 线描 / 素描)有总量预算(约 13 万 / 10 万 / 9 万条), 超出时按工序权重等比稀释,保持各阶段比例而不是砍掉某一层。这样把「细腻程度」 拉到极端值时数据量不会失控(油画最细档原本 22.7MB,现在约 12.5MB,画质无可察觉损失)。

另外批量拼接笔触用的是分块追加而不是 arr.push(...big)——后者在笔触数量大时 会超出引擎的参数栈上限,抛 Maximum call stack size exceeded。

部署到 GitHub Pages

仓库本身就是 Pages 站点,main 分支 + GitHub Actions 自动部署,地址是 https://tyuecode.github.io/。

要绑自定义域名:在仓库 Settings → Pages → Custom domain 里填域名, GitHub 会自动往仓库写一个 CNAME 文件(不用手动建)。另外还需要在你的 DNS 那边加一条 CNAME 记录指向 tyuecode.github.io。

注意:如果仓库里有 CNAME 文件、但 Pages 设置里的域名为空,两者会打架, 访问可能解析失败。换域名或撤域名时,建议两边一起改。

其他静态托管(Cloudflare Pages / Netlify / Vercel)同样适用——几个静态文件,无构建步骤。

本地开发

python -m http.server 8000
# 访问 http://localhost:8000/

注意:不要用 file:// 直接双击 index.html,Web Worker 和 fetch 会被浏览器拦截。

许可证

MIT