TRAE 全链路实战:做了一个警示牌一键生成器:纯 Canvas、单文件、零依赖

从需求到落地,聊聊如何用一个 HTML 文件做出支持 A4 标准出图、10 种形状、自动排版的警示牌工具。

一、为什么做这个

日常办公里经常需要打印各种警示牌------"请勿乱动""小心地滑""禁止吸烟"......每次都要打开设计软件、调尺寸、找图标、排版,做一张简单的牌子要花十几分钟。网上的在线工具要么有水印、要么要登录、要么导出的尺寸不够打印。

于是干脆自己写一个:纯本地、单文件、双击即用,打开浏览器就能做出 A4 大小、300DPI 的警示牌图片,直接打印。

10 种形状 · 10 套配色 · 20 个常用模板 · 60+ 表情图标 · A4/A3/A5 标准尺寸 · 一键 PNG / JPG

二、能做什么

功能 说明
🎨 可视化编辑 左侧控制面板,右侧实时预览。改文字、换形状、调颜色,所见即所得。
📐 标准尺寸出图 A4 / A3 / A5 / 名片 / 自定义,按 DPI 换算像素,A4@300DPI = 2480×3508px,直接打印清晰。
🔷 10 种形状 矩形、圆角、圆形、椭圆、三角、六边形、菱形、盾牌、星形、拱形,全部矢量绘制。
🚫 国标图标 禁止圈、警告三角、指令圆、安全圆四种外框 + 表情,组合出标准禁烟/触电等标识。

三、技术实现核心

3.1 单文件架构

整个工具就是一个 .html 文件:HTML 结构 + CSS 样式 + 原生 JS,没有任何外部依赖。不加载 CDN、不打包、不构建,双击就能跑。这对一个"随时打开就能用"的小工具来说体验最好。

less 复制代码
const state = {
  text:'请勿乱动', sub:'谢谢配合', icon:'✋', iconStyle:'prohibited',
  theme:'yellow-black', shape:'rounded', sizeKey:'a4', orient:'portrait',
  dpi:300, insetPct:0, iconPct:26, bwPct:12, borderOn:true, bold:true, crop:false,
  bg:'#FFD400', fg:'#141414', bd:'#141414', font:'yahei'
};

所有配置收拢在一个 state 对象里,修改任意字段后调用 scheduleRender() 重绘预览。导出时用同样的 drawSign() 函数画到一张高分辨率 canvas 上------预览和导出共用同一套渲染逻辑,保证所见即所得。

3.2 参数化布局

这是整个项目最关键的设计。所有尺寸都按画布短边 m = Math.min(W,H) 的比例计算:

javascript 复制代码
function makePath(shape, W, H){
  const m = Math.min(W,H);
  switch(shape){
    case 'circle':   p.arc(cx, cy, m/2*0.98, 0, TAU); break;
    case 'triangle': roundPoly(p, [[cx,H*0.015],[W*0.975,H*0.965],...], m*0.045); break;
    case 'star': {
      const R = m/2*0.97, rIn = R*0.382;  // 标准五角星内外半径比
      ...
    }
  }
}

这样无论预览是 400px 还是导出是 2480px,渲染结果完全一致。星形只取短边做半径,避免在 A4 竖版上被纵向拉长变形------这是早期版本踩过的坑。

💡 小提示: 正五角星的内外半径比是 0.382(黄金比例的倒数减一),用这个比值画出来才是"标准"的五角星。

3.3 文字自适应排版

警示牌文字不能被形状边缘裁掉,也不能溢出。核心思路:先算出形状内最大内接矩形,再在矩形里自动换行 + 缩字号。

实现用了一个小技巧------二分法搜索 + 网格采样:

ini 复制代码
function rectInside(path, x, y, w, h){
  const N = 6;  // 6×6 = 49 个采样点
  for(let i=0;i<=N;i++) for(let j=0;j<=N;j++)
    if(!probe.isPointInPath(path, x+w*i/N, y+h*j/N)) return false;
  return true;
}
// 二分搜索最大缩放比例 s,使候选矩形完全落在形状内
let lo=0.06, hi=1;
for(let i=0;i<22;i++){
  const mid=(lo+hi)/2;
  if(rectInside(path, cx-cw*mid/2, cy-ch*mid/2, cw*mid, ch*mid)) lo=mid; else hi=mid;
}

采样点从早期的 3×3 加到 6×6(49 点),因为星形这种凹多边形,3×3 会漏掉凹边导致文字被裁。二分 22 次后精度足够,肉眼无感知。

3.4 图标系统

图标 = 表情 + 外框。外框用矢量画四种国标样式:

外框样式 绘制方式 适用场景
禁止圈 红色圆圈 + 45° 斜杠 禁止吸烟、禁止入内
警告三角 黄色填充三角形 + 黑边 当心触电、小心地滑
指令圆 蓝色实心圆 保持安静、请随手关门
安全圆 绿色实心圆 安全出口、急救站

选「禁止圈」+ 🚬,就是标准禁烟标识。表情用 Segoe UI Emoji 字体直接 fillText 画到 canvas 上,无需图片资源。

四、三个有意思的优化

4.1 导出裁掉形状外空白

A4 竖版上画一个圆形,上下会留大片空白。导出时如果想贴边用,就需要裁掉。做法是把形状画到一张低分辨率蒙版上,反查实际占用的外框:

javascript 复制代码
function cropRect(shape, W, H){
  // 缩放绘制到 640px 蒙版,扫描非透明像素求 min/max
  const N=640, k=N/Math.max(W,H);
  // ... 扫描 alpha 通道 ...
  // 加上半描边补偿,避免裁掉边框
  const pad = lineWidth/2 + 1.5/k;
  return {x, y, w, h};
}

蒙版只有 640px,扫描极快;描边有一半在路径外侧,所以裁切框要外扩半个线宽,否则边框会被切掉。

4.2 一键复制到剪贴板

比下载更顺手的是直接复制。用 ClipboardItem 传 Promise 值,保住用户手势:

ini 复制代码
const item = new ClipboardItem({
  'image/png': new Promise(res => canvas.toBlob(res, 'image/png'))
});
navigator.clipboard.write([item]);

⚠️ 注意: Clipboard API 要求页面处于聚焦状态,非 https 环境下部分浏览器会拒绝。所以我保留了下载按钮作为兜底。

4.3 设置自动记忆

用户调好的文字、形状、颜色,刷新后不丢。用 localStorage 持久化 + 字段校验,防止脏数据搞崩页面:

ini 复制代码
state.bwPct = Math.min(100, Math.max(0, +s.bwPct || 12));
if(SHAPE_IDS.indexOf(s.shape) >= 0) state.shape = s.shape;
if(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(s.bg)) state.bg = s.bg;

五、踩坑笔记

坑 1:边框粗细 10× 错配

滑块标签显示 0.8%,实际画的是 8%------A4 上约 16.8mm 巨粗边框,圆形直接溢出画布。根因是数值语义不统一 :绘制用 bwPct/100,标签却按 bwPct/100 显示成了 0.8%。统一改为千分比(/1000),滑块量程扩到 0--100,标签和绘制完全一致。

坑 2:五角星变形

早期把星形按 rx=W*0.485, ry=H*0.485 分别缩放,A4 竖版上被纵向拉长、横版被压扁。改回短边统一半径后,无论横竖都是正五角星。

坑 3:凹形文字被裁

3×3 采样点覆盖不了星形凹边,副提示语被截断。加到 6×6(49 点)后彻底解决。

坑 4:querySelector vs getElementById

重写文件时把 $ = document.querySelector,但调用都是 $('text')(传 id 不带 #),结果所有元素查找返回 null,整页初始化失败。改回 getElementById 就好了------重构时注意 API 语义。

六、和 TRAE 协作的过程

这个工具从头到尾是我和 AI 协作完成的。整个过程比我预想的顺畅,也有几个值得分享的点。

流程大致是这样的: 我用自然语言描述需求 → AI 出第一版代码 → 我在浏览器里试用 → 发现问题反馈给 AI → AI 定位并修复。来回几轮就成型了。

几个协作中的真实片段:

  • 五角星变形:我只说了一句"五角星的形状好像有点问题",AI 就定位到是半径按宽高分别缩放导致的,改成短边统一半径后规则了。我提一句,它给方案,效率比自己查资料高很多。
  • 边框粗细 10× 错配 :这个 bug 比较隐蔽------圆形在 A4 上外框溢出,我以为是裁切逻辑错了。AI 通过浏览器执行 cropRect('circle',2480,3508) 拿到数值,反推发现裁切框是对的,问题出在描边粗细的标签和实际绘制差了 10 倍。这种"用数值反推根因"的排查方式,是 AI 擅长的。
  • querySelector 踩坑 :我重写文件后页面全空,AI 用浏览器自动化抓控制台错误,秒定位到 $('text') 返回 null,三两下就修好。
  • 验证环节:AI 会自己起一个本地静态服务器,用浏览器自动化跑断言------比如"星形裁切框宽高是否在预期范围""localStorage 是否存了正确字段"。这种可复现的验证让我很放心。

整体感受:AI 不是替我写代码,而是把"想方案→写代码→跑验证→修 bug"这个循环压缩得很快。我负责提需求、做判断、试体验,它负责出实现、做验证。碰到不确定的地方,我会让它解释清楚再决定要不要改。

七、总结

这个小工具前前后后迭代了几轮,核心收获有三点:

  1. 参数化布局是让"小预览 = 大导出"的关键,所有尺寸按比例算,渲染逻辑只写一遍。
  2. 几何问题用几何思路解:内接矩形用二分+采样,裁切用蒙版反查,比硬编码坐标靠谱得多。
  3. 单文件零依赖对工具类产品体验极好,分享给同事直接发文件就行。

文件就是一个 HTML,双击打开就能用。想要批量出图、竖排文字、文字描边这些,后面可以继续加。

如果你也经常需要做各种标识牌,不妨自己动手写一个------比每次找模板快多了。

相关推荐
小万7422 小时前
God‘s Eye View 深度解析:15 路公开信号 + CesiumJS 的浏览器级 OSINT 控制台,60 帧渲染的“真实间谍卫星模拟器”
前端
IT星宿2 小时前
浏览器标签页全是一个图标:我用名字哈希生成 favicon,让每个项目都有身份标识
前端
全栈项目管理程序猿2 小时前
ArcGIS JS 基础教程(32):体元系列 - VoxelSlice 体元切片
前端·javascript
拖孩4 小时前
AI 帮我写的小程序「厨菜记」上线满月了,挣了两百块!!!
前端·后端·微信小程序
用户360055579004 小时前
Kestrel_VLLM - 13 · 为什么排除 MoE FFN Tiling 与 Tensor Core
前端
Hilaku4 小时前
AI 流式输出都用 SSE,什么时候必须换 WebSocket?
前端·javascript·程序员
奇牙coding5 小时前
Codex CLI 升级后多文件编辑静默失效:config.yaml 枚举变更排查与修复指南
java·前端·javascript·ai
小羊没烦恼!5 小时前
Ajax与JSON的一些总结
android·java·前端·数据库·mysql·算法·okhttp
程序员老赵5 小时前
Docker 部署 Filestash:轻松搭建浏览器文件管理平台
前端·docker·开源