网站小图标怎么制作让 SVG、Canvas 预览与 PNG 导出使用同一份绘制源深圳技术大学代码仓库制作一个简单 Favicon 不一定需要设计软件。对原型、个人页面或内部小工具一个字母加背景色就可以建立基本辨识度。但生成器真正容易出问题的地方通常不是颜色选择而是预览看起来正常下载图片却出现不同圆角、文字位置或尺寸。若预览用 CSSPNG 用 CanvasSVG 又单独拼一套坐标三个结果很快就会分叉。本项目把 SVG 作为唯一绘制描述再把它加载到 Image最后画进 Canvas。用户看到的 Canvas 和下载的 PNG 因此来自同一个渲染结果SVG 下载则直接保存源字符串。输入仅包括文字、背景色、文字色、形状和尺寸输出为 PNG 与 SVG不包含 ICO。先选统一坐标再处理输出尺寸SVG 使用固定的二百五十六单位 viewBox背景矩形和文本都按这一坐标系定位。导出尺寸只改变 SVG 的 width、height 与 Canvas 像素宽高不必为每个尺寸重算所有图形坐标。界面提供十六、三十二、四十八、六十四、一百二十八、二百五十六和五百一十二像素覆盖简单图标制作与较大预览。背景有方形、圆角方形和圆形三种。方形圆角半径为零圆角方形使用固定比例半径圆形半径取边长的一半。圆角之外没有额外填充因此保留透明区域。页面的棋盘式预览背景帮助用户识别透明度但它不是图标本身的一部分下载后不会把预览底纹一起写入图片。文字限制为一到三个可感知字符按照数量选择不同字号控制短文本在图标中的占比。这里使用 Intl.Segmenter 识别字素簇使带连接符的组合 emoji 尽量作为一个字符处理而不是按 UTF-16 代码单元拆开。环境没有该 API 时回退为 Array.from这个回退对复杂组合 emoji 的计数不等同于完整字素分割文档不会把它写成所有旧环境都完全一致。SVG 字符串也是需要保护的输出用户输入的文字会嵌入 text 元素必须先转义与号、尖括号和引号。颜色必须匹配六位 HEX形状必须属于明确枚举尺寸必须属于允许列表。不能因为界面有下拉框就忽略核心校验调用者仍可能直接传入其他字符串。固定模板加受控参数能让 SVG 源码保持可理解的结构。以下代码摘自交付项目的 core.js(function(root){use strict;functionescape(s){returnString(s).replace(/[]/g,c({:amp;,:lt;,:gt;,:quot;,:#39;}[c]));}functiongraphemes(text){returntypeofIntl.Segmenterfunction?[...newIntl.Segmenter(undefined,{granularity:grapheme}).segment(text)].map(xx.segment):Array.from(text);}functionsvg({text,background,foreground,shape,size}){constpartsgraphemes(String(text).trim());if(parts.length1||parts.length3)throwError(请输入 13 个文字或符号);if(![16,32,48,64,128,256,512].includes(Number(size)))throwError(请选择有效图标尺寸);if(![background,foreground].every(v/^#[0-9a-f]{6}$/i.test(v)))throwError(颜色必须为六位 HEX);if(![square,rounded,circle].includes(shape))throwError(无效形状);constradiusshapecircle?128:shaperounded?52:0;constfontparts.length1?150:parts.length2?104:72;returnsvg xmlnshttp://www.w3.org/2000/svg width${size} height${size} viewBox0 0 256 256rect width256 height256 rx${radius} fill${background}/text x128 y134 text-anchormiddle dominant-baselinecentral font-familyArial,Microsoft YaHei,sans-serif font-size${font} font-weight700 fill${foreground}${escape(parts.join())}/text/svg;}root.ToolCore{svg,graphemes};if(typeofmodule!undefined)module.exportsroot.ToolCore;})(globalThis);文本通过 text-anchor 和 dominant-baseline 居中。它们建立统一的对齐规则但不同字体、汉字和 emoji 的字形边界并不相同因此这不是专业光学居中算法。字体使用系统回退同一个 SVG 在不同系统中可能选到不同字形。PNG 固化了当前浏览器的绘制结果SVG 则保留文字与字体引用跨机器再渲染可能出现差别。两种格式的这个边界值得在使用前明确。从 SVG 到 Canvas 是一个异步过程每次配置变化生成新的 SVG包装成 Blob 并建立临时对象 URL等待 Image 加载完成后再绘制。不能在设置 img.src 后立刻 drawImage因为图像可能还没有解码。加载失败需要进入明确错误状态不能继续允许用户下载上一次成功的图标。临时 URL 在成功、失败或旧任务被舍弃后都会释放避免反复调色时积累对象 URL。快速输入与切换尺寸可能让多次渲染同时等待。项目用递增 version 标识本轮任务加载完成时只有当前版本可以写入 Canvas。较早任务即便晚完成也会被忽略。开始新一轮渲染时先禁用下载与复制完成后再恢复使导出动作始终对应一份已完成的图形而不是当前控件与旧画布的混合状态。Canvas 的像素宽高取实际导出尺寸CSS 只负责在页面内限制显示宽度。导出 PNG 使用 toBlob而不是截取页面区域因此不会包含卡片边框、按钮或预览底纹。SVG 下载直接保存生成字符串两种文件名都带尺寸便于区分多次下载。PNG 保留 Alpha 通道圆形图标四角透明。验证下载文件而不只看按钮反馈核心测试检查 SVG 尺寸、圆角、转义、组合 emoji 与非法参数。浏览器操作把文字改为 JS、尺寸改为六十四、形状改为圆形然后读取 Canvas 左上角 Alpha确认透明角落实际存在。接着真正接收 PNG 与 SVG 下载解析 PNG 头中的宽高并检查 SVG 源码中的尺寸与文本。下载成功与内容正确是两项不同证据。无效文本测试使用超过三个字符的输入检查旧导出是否被禁用再快速改成两个不同值最终源码必须对应最新输入。它覆盖了一个容易忽略的交互边界即使最后一张截图正确也不能保证在异步过程中的旧按钮没有暴露错误文件。本工具通过版本检查与按钮状态共同约束这一过程。浏览器还实际检查复制内容并模拟剪贴板拒绝。源码始终保留在页面上复制失败不会被解释成 SVG 生成失败。手机宽度下卡片改为单列画布按可用宽度缩小显示但其内部像素尺寸保持不变因此预览缩放不会偷偷改变下载尺寸。把生成文件用到网站中使用 PNG 时在网页 head 中添加指向实际文件的 icon 链接并按文件尺寸填写相应信息使用 SVG 时应确认目标环境的支持策略。这个工具本身只负责生成图片不会自动替换站点资源或发布网页。小尺寸下三字文本可能难以识别通常更适合单个字母或简洁符号下载前应该切换到实际使用尺寸观察。后续可以增加 ICO 封装、多个尺寸批量下载、文字到路径转换或自定义字体。不过批量导出依然应复用同一份 SVG 规则文字转路径则需要额外字体处理不能只把文件扩展名改成 ICO 或声称 SVG 中的文字已经转曲。保持格式、渲染与导出职责清晰才能让这个小工具在继续扩展时仍然可信。小尺寸与导出生命周期仍要单独观察把预览缩放到很大再判断图标是否清晰会掩盖十六像素下的细节损失。相同文字在大尺寸可读在小尺寸可能只剩几块色块因此实际使用前应切换到目标尺寸必要时减少字符数量。这个建议来自图标使用场景不代表工具进行了专业可读性评分页面没有根据主观审美自动判定哪个图标一定更好。导出按钮只在完成绘制后可用临时下载 URL 延迟释放让浏览器有机会完成文件接收。图像加载用的 URL 则在绘制后释放两类 URL 的生命周期不同不能为了代码简短一创建就立即回收。页面反复修改参数时每次都应该释放不再使用的加载资源避免一个小型生成器因长时间操作积累无意义对象。PNG 与 SVG 保持一致指的是同一参数下、当前浏览器生成过程使用统一绘制源它不表示两个文件的内部数据结构相同也不保证 SVG 到另一台电脑仍选中相同字体。若要跨环境固定字形可考虑转曲或嵌入合规字体这需要额外处理。当前文档把这一限制与已验证的尺寸、透明度分开让用户知道哪些结果来自测试哪些仍依赖使用环境。PNG 的 toBlob 回调异步完成用户可能在这段时间修改图标尺寸。本次复核将尺寸和渲染版本在点击导出时保存文件名对应点击当时的像素尺寸旧任务完成后也不覆盖新预览的状态提示。浏览器场景延迟编码回调在预览改为 128 像素后检查导出的旧快照仍为 64 像素且文件名为 favicon-64.png。运行、文件职责与本次验证记录本项目目录为07-favicon-generator采用原生 HTML、CSS 和 JavaScript没有后端、外部字体或运行时 CDN。直接打开 index.html 可以使用主要功能本次也通过真实浏览器验证了 file 协议下的主要交互。更推荐进入该目录执行下列命令以 localhost 方式运行便于获得一致的网页源和浏览器 API 行为。静态服务只监听本机回环地址不需要配置账户或上传个人数据。node serve.cjs.4279# 打开 http://127.0.0.1:4279/node--test test.cjsindex.html 负责语义结构和资源引用styles.css 负责卡片、表单与移动宽度布局core.js 保存本工具独立逻辑同时导出给 Node 测试app.js 负责事件绑定、状态变化和页面渲染。ui.js 提供状态提示、复制及本地下载等通用能力。脚本使用 defer 并按核心、通用界面、应用顺序加载既等待 DOM 就绪也让依赖关系明确。修改核心规则时先运行对应测试再检查界面是否继续解释同一规则。测试结果分成两层Node 核心测试本次执行2 项2 项通过、0 项失败、0 项跳过真实 Chrome 155.0.8059.84 浏览器验证6 项通过、0 项失败、0 项跳过。浏览器的每项记录是一个包含多个断言的验证场景不应与纯逻辑测试条数混算成同一种数量。原始记录保存在 docs/unit-results.tap 与 docs/browser-results.json可以核对日期、场景和控制台检查而不是只引用文章中的结论。本次浏览器场景如下参数联动PNG 与 SVG 导出尺寸及透明背景正确非法文本禁用旧导出快速修改最终显示最新版本真实剪贴板复制与拒绝权限提示PNG 异步编码期间修改尺寸文件名与点击时像素尺寸一致file:// 独立加载并执行主要交互无页面或控制台错误390px 手机宽度没有页面横向溢出浏览器检查通过 Playwright 驱动实际 Chrome 页面执行输入、点击、键盘或文件操作并保存本文三张截图。相关脚本位于工具合集的 scripts/browser-check.cjs使用合集复现时先运行 npm start再在另一终端安装对应 Playwright 依赖并执行 npm run test:browser。本次使用环境已有运行库没有要求运行页面的读者安装构建链。单独项目只需现代浏览器Node 用于本地静态服务与纯逻辑测试两个用途彼此独立。本次页面错误与控制台错误记录均为空。三百九十像素宽度检查确认页面没有整体横向溢出局部代码区、预览或表格允许自身滚动。这只是当前 Chrome 环境的布局证据并非已经实测所有手机、旧浏览器或辅助技术。其他现代浏览器预计可以使用相关原生 API但 Firefox、Safari 与实体移动设备本次未实测若业务依赖特定平台应按目标环境追加验证。
阅读完成 · 觉得有帮助?