wired-icon:用 Web Component 把任意 SVG 图标一键转成手绘素描风
2026/9/23 16:25:16 网站建设 项目流程
  • UI组件
  • 前端

【免费下载链接】wired-elements

Collection of custom elements that appear hand drawn. Great for wireframes or a fun look.

项目地址:https://gitcode.com/gh_mirrors/wi/wired-elements
点击查看免费下载

wired-icon是 wired-elements 实验包中的一个 Web Component,它能把任意标准 SVG 图标实时转换为带有手绘(hand-drawn / sketchy)质感的版本,非常适合线框稿(wireframe)或追求随性趣味的界面。读完本文,你将掌握它的安装方式、config配置参数、Light DOM 约束、无障碍处理,以及如何把它嵌套进wired-icon-button中,并理解其底层转换原理与源码实现。

组件定位:从"图标"到"手绘图标"

wired-icon的核心能力可以概括为一句话:把任何 SVG 转换为其手绘、素描版本(见 experimental/wired-icon/README.md)。它不会要求你使用某个特定图标库,而是接受你传入的任意svg片段,再通过底层绘制库(wired-lib)基于 roughjs 重新渲染成抖动的、不完美的线条与填充,形成类似手绘草稿的视觉风格。

在项目内它还有一层特殊身份:它是wired-mat-icon的基础库wired-mat-icon内置了几乎全套 Material Design 图标集,本质上就是在wired-icon的转换能力之上,预置了图标路径数据(参见 wired-mat-icon/README.md)。因此理解wired-icon,也就理解了整个 wired 图标体系的地基。

安装与引入

按 README 的说明,安装只需一条命令:

npm i wired-icon

引入方式有两种,任选其一:

  • 直接在 HTML 页面中以 ES Module 方式加载:
<script type="module" src="wired-icon/lib/wired-icon.js"></script>
  • 或在你自己的模块脚本中 import:
import "wired-icon";

从 experimental/wired-icon/package.json 可以看到,该包以lib/wired-icon.js作为入口("main": "lib/wired-icon.js"),运行时依赖lit-element(^2.3.1)与wired-lib(^2.0.0)——前者负责组件生命周期与属性响应,后者负责把 SVG 渲染成手绘版本。构建产物lib/由 TypeScript 编译生成("build": "rm -rf lib && tsc"),仓库内已包含编译后的 lib/WiredIcon.js 可供直接查看。

基础用法:用<wired-icon>包裹你的 SVG

wired-icon的使用方式非常直接:把任意 SVG 图标放进<wired-icon>标签内部,它会在渲染时自动完成转换。README 给出了一个完整的示例:

<wired-icon config='{"fillStyle": "zigzag", "fill": "#A4C639", "hachureGap": "1.5", "fillWeight": "0.9"}'> <svg width="70" height="70" viewBox="-1 -1 24 26"> <path d="M6 18c0 .55.45 1 1 1h1v3.5c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5V19h2v3.5c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5V19h1c.55 0 1-.45 1-1V8H6v10zM3.5 8C2.67 8 2 8.67 2 9.5v7c0 .83.67 1.5 1.5 1.5S5 17.33 5 16.5v-7C5 8.67 4.33 8 3.5 8zm17 0c-.83 0-1.5.67-1.5 1.5v7c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5v-7c0-.83-.67-1.5-1.5-1.5zm-4.97-5.84l1.3-1.3c.2-.2.2-.51 0-.71-.2-.2-.51-.2-.71 0l-1.48 1.48A5.84 5.84 0 0 0 12 1c-.96 0-1.86.23-2.66.63L7.85.15c-.2-.2-.51-.2-.71 0-.2.2-.2.51 0 .71l1.31 1.31A5.983 5.983 0 0 0 6 7h12c0-1.99-.97-3.75-2.47-4.84zM10 5H9V4h1v1zm5 0h-1V4h1v1z"/> </svg> </wired-icon>

这个例子同时展示了两个关键点:

  1. 图标内容照常书写——pathd数据来自普通 Material Design 图标,无需任何改写;
  2. 效果通过config属性定制——这里配置了zigzag锯齿填充、草绿色#A4C639、较密的排线间距等,渲染出的就是"手绘填色"的效果。

仓库中的演示页面 experimental/icon.html 提供了大量可直接运行的真实案例:来自 Vaadin 图标集的星形、Facebook 图标、Wi-Fi 状态图标、信封、猫爪(paw print)等,均以不同config组合展示,是调参时最好的灵感来源。

config属性:手绘效果的完整控制面

configwired-icon唯一的核心属性,类型为可选的对象,用来配置 roughjs 的渲染选项。README 明确提示:完整选项列表可参考 roughjs 的 wiki(Options 一节),并结合示例页寻找灵感。

默认值:roughness 0.1

README 特别强调了一个默认值:

默认 roughness 被设置为 0.1,这在对图标而言大多数时候都是最合适的一个值。

这一默认值在源码中有直接证据。WiredIcon.ts 顶部定义了:

const DEFAULT_CONFIG: Options = { roughness: 0.1, };

roughness控制线条的"抖动/潦草"程度——数值越大线条越狂野,而 0.1 这个低值让图标在保留手绘感的同时仍能清晰可辨,非常适合小尺寸图标。在connectedCallback中,用户配置会与默认配置合并后生效:

wiredSvg(svg, {...DEFAULT_CONFIG, ...this.config});

也就是说,你未指定的参数全部使用内置默认,只有显式传入的键会被覆盖。

属性反射(reflect)

config被声明为:

@property({ type: Object, reflect: true }) config: Options = DEFAULT_CONFIG;

reflect: true意味着通过 JavaScript 修改config属性时,会同步反映到 DOM 属性上;反过来,在 HTML 中直接写config属性也能被读取。这为后续的程序化控制(例如在框架或自定义组件中动态换肤)留好了接口——wired-mat-icon的 README 就演示了wiredMatIcon.config = {fill: 'red', fillWeight: 1}这类用法。

常用参数速查(来自仓库示例的实测组合)

下表整理了 README 与 experimental/icon.html 中实际出现过的参数及其含义:

参数作用示例取值
roughness线条抖动/潦草程度,默认0.10.10.20.3
fill填充颜色"#A4C639""lightblue""gray""blue""red"
fillStyle填充纹理风格"hachure"(默认排线)、"zigzag"(锯齿)、"cross-hatch"(交叉排线)、"solid"
fillWeight填充线条的粗细0.50.80.9
hachureGap排线间距(越小越密)11.5
hachureAngle排线角度2050
stroke描边颜色默认"#000";设为"transparent"可去描边
strokeWidth描边粗细0.30.8

典型组合示例(来自示例页)——柔和浅蓝填充:

<wired-icon class="icon" config='{"fill": "lightblue", "hachureGap": "1.5", "fillWeight": "0.9", "roughness": "0.1"}'> <svg viewbox='-1 -1 18 18'> <path d="M7.5 12.2c-2.3 0-4.2-1.9-4.2-4.2s1.9-4.2 4.2-4.2 4.2 1.9 4.2 4.2c0.1 2.3-1.9 4.2-4.2 4.2z..."></path> </svg> </wired-icon>

小贴士:阅读示例页时注意,手绘线条通常比原图略"胖",示例中大量图标使用viewbox='-1 -1 18 18'这类带负坐标起点的视图框,正是为了给抖动线条留出边距。

Light DOM 约束:必须遵守的书写规则

wired-icon不使用 Shadow DOM。看 WiredIcon.ts 的实现:

createRenderRoot() { // No use for shadow DOM return this; }

因此你写在标签内部的内容就是 Light DOM,connectedCallback会直接执行this.querySelector('svg')找到第一个svg节点交给wiredSvg处理。这也带来三条硬性约束:

约束一:必须用<svg>包裹

转换的输入是一个svg节点,裸元素无法被识别。

无效 Light DOM 示例 1(没有 svg 标签包裹):

<circle cx="16.5" cy="5.5" r="2.5"/>

约束二:只能有一层深度

README 原文要求"only one level of depth"。也就是说,svg的直接子元素会被转换,但嵌套在<g>等容器内的元素不会被转换,而是原样保留。

无效 Light DOM 示例 2(<g>内的 rect 不会被转换):

<svg viewbox='0 0 24 24'> <g> <rect x="3" y="8" width="18" height="13"></rect> <rect x="1" y="3" width="22" height="5"></rect> </g> <line x1="10" y1="12" x2="14" y2="12"></line> </svg>

上面的示例中,两个<rect>因为被<g>包裹而保持原样,只有<line>会被手绘化。

有效的 Light DOM 示例:

<svg viewbox='0 0 24 24'> <rect x="3" y="8" width="18" height="13"></rect> <rect x="1" y="3" width="22" height="5"></rect> <line x1="10" y1="12" x2="14" y2="12"></line> </svg>

约束三:支持的基本图形集合

wired-icon能转换以下 SVG 标签:

  • circle(圆)
  • ellipse(椭圆)
  • line(直线)
  • path(路径)
  • polygon(多边形)
  • polyline(折线,⚠️ 转换结果将与polygon相同,即会被当作闭合多边形处理)
  • rect(矩形)

其余任何标签都会被"原样包含"在转换后的 SVG 中——这一点对实践很重要:如果图标里存在wired-icon不支持的元素,它不会被丢弃,而是以原始形式混入最终结果。

从仓库底层的 src/wired-lib.ts 可以看到这一能力集对应的绘制函数:rectanglelinepolygonellipsearc分别把基本几何图形交给 roughjs 的 renderer 生成手绘路径(roughRectangleroughLineroughPolygonroughEllipseroughArc),再用opsToPath把渲染操作(move / bcurveTo / lineTo)拼成最终的path元素。值得留意的是其中对尺寸的处理细节,例如rectangle会向内收缩 2px(rectangle(x + 2, y + 2, width - 4, height - 4, ...))、ellipse会按尺寸收缩 1~4px,目的同样是给手绘抖动留出视觉边距——这印证了示例中负起点 viewBox 的用法并非偶然。

样式与缩放

README 的 Styling 一节给出了两条指引:

  1. 缩放:在wired-icon元素或内部svg标签上通过 CSS 设置width/height即可改变图标尺寸。
  2. 变色:不要用 CSS 改颜色,请优先使用config属性中的颜色参数(如fillstroke)。原因在于手绘效果由 roughjs 在渲染期生成路径,CSS 颜色规则无法作用到这些生成路径的填充/描边上,所以统一走config才是可控的方式。

组合玩法:放进wired-icon-button

wired-icon可以与 wired-elements 家族的其他组件无缝嵌套,README 专门演示了放入wired-icon-button的用法——这也是图标组件最常见的落地场景:

<style> .icon-button{ display: block; width: 30px; } </style> <wired-icon-button elevation="5"> <wired-icon class="icon-button" config='{"strokeWidth": "0.3", "fill": "blue", "fillStyle": "cross-hatch"}' > <svg viewbox="-1 -1 18 18"> <path d="M9 11h-3c0-3 1.6-4 2.7-4.6 0.4-0.2 0.7-0.4 0.9-0.6 0.5-0.5 0.3-1.2 0.2-1.4-0.3-0.7-1-1.4-2.3-1.4-2.1 0-2.5 1.9-2.5 2.3l-3-0.4c0.2-1.7 1.7-4.9 5.5-4.9 2.3 0 4.3 1.3 5.1 3.2 0.7 1.7 0.4 3.5-0.8 4.7-0.5 0.5-1.1 0.8-1.6 1.1-0.9 0.5-1.2 1-1.2 2z"/> <path d="M9.5 14c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2s2 0.895 2 2z"/> </svg> </wired-icon> </wired-icon-button>

这段代码同时示范了两个实践要点:

  • 通过给内部的wired-icon加 CSS 类并设置width: 30px,让图标尺寸匹配按钮的视觉比例;
  • 图标路径可以有多个(这里是"灯泡"主题的两个path),它们各自独立被手绘化。

experimental/icon.html中同样的组合还展示了wired-mat-icon版本(icon="edit"),两种写法在按钮场景下可以互换。

无障碍:为图标补充语义标签

图标类组件如果只提供纯视觉内容,对使用屏幕阅读器的用户是不友好的。README 推荐在svg内使用aria-labelledby+<title>的方式来提供可读的图标名称:

<wired-icon class="icon" config='{"fill": "gray", "fillStyle": "cross-hatch", "hachureGap": "1.5", "fillWeight": "0.5", "roughness": "0.1"}'> <svg viewBox="0 0 24 24" aria-labelledby="icon-label"> <title id="icon-label">Icon of a pet footprint</title> <circle cx="4.5" cy="9.5" r="2.5"/> <circle cx="9" cy="5.5" r="2.5"/> <circle cx="15" cy="5.5" r="2.5"/> <circle cx="19.5" cy="9.5" r="2.5"/> <path d="M17.34 14.86c-.87-1.02-1.6-1.89-2.48-2.91-.46-.54-1.05-1.08-1.75-1.32-.11-.04-.22-.07-.33-.09-.25-.04-.52-.04-.78-.04s-.53 0-.79.05c-.11.02-.22.05-.33.09-.7.24-1.28.78-1.75 1.32-.87 1.02-1.6 1.89-2.48 2.91-1.31 1.31-2.92 2.76-2.62 4.79.29 1.02 1.02 2.03 2.33 2.32.73.15 3.06-.44 5.54-.44h.18c2.48 0 4.81.58 5.54.44 1.31-.29 2.04-1.31 2.33-2.32.31-2.04-1.3-3.49-2.61-4.8z"/> </svg> </wired-icon>

注意这里同时用到了 4 个circle和 1 个path的混合结构,且全部位于svg直接子层——完全符合前面说的 Light DOM 约束,这也说明真实图标往往是"多元素混合"的形态。可以推断,aria-labelledby<title>的关系在转换后仍会保留,因为它们是svg属性与子节点的一部分(README 亦将其作为官方推荐做法)。

图标素材从哪来

README 提供了一份"有主观倾向、非穷尽"的高质量免费图标来源清单,这些来源的图标都是标准 SVG 结构,与wired-icon兼容:

  • Google Material Design SVG Icon Repo:Material 官方 SVG 精灵图集,图标均为标准path/circle结构,拿来即用;
  • Vaadin Icons:Vaadin 组件库配套的图标集,同样质量高且免费。

如果使用 Material 图标,更高效的做法是直接上wired-mat-icon——它把几乎全部 Material 图标打包成了内置 iconset,通过icon="android"这样的字符串即可引用,省去手动寻找 SVG path 的麻烦(参见 wired-mat-icon/README.md)。但需要注意,wired-mat-icon的 README 明确警告:它会对打包体积产生较大影响(因为内置了全套图标路径),生产环境建议优先用wired-icon自行挑选需要的 SVG 来做体积优化。

源码结构解读:30 行代码背后的设计

wired-icon的核心实现非常精简。整个包只有两个源文件:

1. src/WiredIcon.ts —— 组件类定义

import { Options, wiredSvg } from 'wired-lib/lib/wired-lib'; import { LitElement, css, CSSResult, property } from 'lit-element'; const DEFAULT_CONFIG: Options = { roughness: 0.1, }; export class WiredIcon extends LitElement { @property({ type: Object, reflect: true }) config: Options = DEFAULT_CONFIG; static get styles(): CSSResult { return css` :host { display: block; } `; } connectedCallback() { super.connectedCallback(); const svg = this.querySelector('svg'); if (svg) { wiredSvg(svg, {...DEFAULT_CONFIG, ...this.config}); } } createRenderRoot() { // No use for shadow DOM return this; } }

几个值得注意的实现细节:

  • 转换时机connectedCallback中执行wiredSvg(svg, config),即元素挂载到 DOM 时一次性完成 SVG 的手绘化重写;
  • 空保护if (svg)判断保证没有svg子节点时组件不会报错,只是安静地不执行转换;
  • 不渲染模板:没有render()方法、没有 shadow DOM,组件生命周期只负责"找到 svg → 交给 wiredSvg"这件事。

2. src/wired-icon.ts —— 自定义元素注册

import { customElement } from 'lit-element'; import { WiredIcon } from './WiredIcon'; // We separate the class from its registration as a custom element, // so that WiredIcon class can be extended. // Otherwise, CustomElementRegistry would register it twice and would throw an error. window.customElements.get('wired-icon') || customElement('wired-icon')(WiredIcon); export { WiredIcon };

这里刻意把"类定义"与"自定义元素注册"拆成两个文件,并用window.customElements.get('wired-icon') || ...做幂等注册:既允许他人继承WiredIcon类扩展新组件,又避免重复注册抛错。这是wired-mat-icon得以在其之上构建的关键前提。

3. 工程配置:tsconfig.json 继承仓库根配置,输出目录指向./lib;package.json 声明了依赖与build/tsc:watch脚本。需要自行构建时,在该包目录下执行npm run build即可重新产出lib/

注意事项小结

  • wired-icon位于仓库的experimental/目录,属于实验性组件,功能稳定但仍在演进,正式项目接入前建议先在目标浏览器中实测;
  • polyline会被当作polygon处理(闭合),使用折线类图标时留意结果差异;
  • svg内嵌套<g>的分组元素不会被转换,复杂图标请先拍平结构;
  • 追求极致手绘效果时,roughness可从默认的0.1向上试探,但注意过大的值会让小尺寸图标难以辨认;
  • 生产环境优先用wired-icon按需选取 SVG,把wired-mat-icon视为原型/开发阶段的效率工具(其内置 iconset 体积较大)。

至此,从安装、配置、约束到源码原理,wired-icon的完整使用路径已经打通:只需一个<svg>和一个config,你就能在任何页面里拥有一整套手绘风图标体系

  • UI组件
  • 前端

【免费下载链接】wired-elements

Collection of custom elements that appear hand drawn. Great for wireframes or a fun look.

项目地址:https://gitcode.com/gh_mirrors/wi/wired-elements
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询