wired-divider 手绘风格分隔线组件:从安装配置到源码绘制原理全解析
2026/9/24 4:52:24 网站建设 项目流程
  • 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-divider是 wired-elements 组件库中用于在页面区块之间绘制一条"手绘感"分割线的轻量级 Web Component。本文以 docs/wired-divider.md 为骨架,结合 源码实现 深入讲解它的安装方式、elevation属性、CSS 定制技巧以及底层基于 Rough.js 的绘制与响应式重绘机制,读完即可在 wireframe、原型草图或趣味风格页面中直接使用并自由调参。

组件简介:用一条手绘线划分内容区块

wired-divider在页面中渲染一条略带抖动、具有手绘笔触的水平分隔线,用于替代传统的<hr>,非常适合线框图(wireframes)、低保真原型(mockups)或任何追求"随手画"风格的界面。该组件自 2.0.0 版本(2019 年 10 月)加入 wired-elements(见 CHANGELOG.md),当前仓库包版本为 3.0.0-rc.7(见 package.json)。

组件本身不包含任何可见的 HTML 内容,而是通过自定义元素<wired-divider>在内部生成一个 SVG,再经由 Rough.js 的绘图算法输出带随机抖动的路径。它由 WiredDivider 类 实现,继承自WiredBase(src/wired-base.ts)。

安装与引入

通过 npm 安装

把 wired-elements 添加到你的 JavaScript 项目:

npm i wired-elements

安装完成后即可从包入口导入WiredDivider

import { WiredDivider } from 'wired-elements'; // 或者按需只加载单个组件: import { WiredDivider } from 'wired-elements/lib/wired-divider.js';

两种方式都会注册<wired-divider>自定义元素。第一种方式经由包总入口 src/wired-elements.ts 一次性导出所有组件;第二种方式直接加载单个编译产物lib/wired-divider.jslib目录由npm run build调用 TypeScript 编译生成,编译配置见 tsconfig.json)。

通过 CDN 直接引入

不经过打包工具时,可以在 HTML 页面中直接加载 ES Module:

<script type="module" src="https://unpkg.com/wired-elements/lib/wired-divider.js?module"></script>

加载后,页面上所有<wired-divider>标签都会被自动升级为手绘分隔线组件。

在 HTML 中使用

最简单的用法是把<wired-divider>放在两个内容区块之间。原文档给出的示例:

<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> <wired-divider></wired-divider> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>

仓库中提供了可直接运行的完整示例页面 examples/divider.html,它通过<script type="module" src="../lib/wired-divider.js"></script>引入本地构建产物,并用两段 Lorem ipsum 文本夹着一个<wired-divider></wired-divider>来演示效果。参照该示例,你的页面最小结构为:

<!DOCTYPE html> <html> <head> <script type="module" src="https://unpkg.com/wired-elements/lib/wired-divider.js?module"></script> </head> <body> <p>上方段落……</p> <wired-divider></wired-divider> <p>下方段落……</p> </body> </html>

分隔线会自动撑满宿主元素(display: block)的可用宽度,并在组件首次渲染或容器尺寸变化时按当前宽度重新绘制。

属性详解:elevation

wired-divider只暴露一个属性:

属性类型取值范围默认值说明
elevationNumber1~5(含边界)1绘制的手绘线条数

elevation表示组件绘制几条叠加的抖动线条。值为 1 时只画一条线;值越大线条越"浓重",视觉上像多层描边,可用作更强的视觉分隔强调。

<!-- 单条线(默认) --> <wired-divider></wired-divider> <!-- 双线分隔 --> <wired-divider elevation="2"></wired-divider> <!-- 五条线,最浓重 --> <wired-divider elevation="5"></wired-divider>

从源码看,elevation@property({ type: Number })声明的一个数字属性(src/wired-divider.ts#L8),并且超出 1~5 范围的值会被钳制。绘制与尺寸计算两处都执行了同样的钳制逻辑:

const elev = Math.min(Math.max(1, this.elevation), 5);

也就是说,即使传入elevation="10"elevation="0",实际绘制仍按 5 条或 1 条处理,而不是抛出错误。由于它是响应式属性,运行时通过 JS 修改也会触发重绘:

const divider = document.querySelector('wired-divider'); divider.elevation = 3; // 立即变为 3 条线

自定义外观:颜色与线宽

组件遵循currentColor继承机制,因此通过 CSS 的color属性即可改变分隔线颜色,无需任何专属样式变量:

wired-divider { color: #b03060; /* 任意文字颜色都会作用于分隔线 */ }

原因在基类样式 src/wired-base.ts#L24-L28 中:所有<path>均设置了stroke: currentColor;stroke-width: 0.7;。如果希望更粗的笔触,可以直接覆盖路径样式:

wired-divider path { stroke-width: 1.5; }

此外,基类样式还规定宿主元素默认opacity: 0,只有当组件完成首次绘制并加上wired-rendered类后才淡入显示(src/wired-base.ts#L6-L12),避免用户看到绘制前的空白或半成品闪烁。

源码原理:手绘线是如何画出来的

画布尺寸与线条布局

WiredDivider的重绘流程由基类WiredBase.wiredRender()驱动(src/wired-base.ts#L44-L59):先通过canvasSize()计算画布尺寸,再调用抽象方法draw()绘制。divider 的实现非常直观(src/wired-divider.ts#L30-L41):

protected canvasSize(): Point { const size = this.getBoundingClientRect(); const elev = Math.min(Math.max(1, this.elevation), 5); return [size.width, elev * 6]; } protected draw(svg: SVGSVGElement, size: Point) { const elev = Math.min(Math.max(1, this.elevation), 5); for (let i = 0; i < elev; i++) { line(svg, 0, (i * 6) + 3, size[0], (i * 6) + 3, this.seed); } }

可以拆解出几个关键结论:

  • 高度公式:画布高度 =elevation × 6像素,即每条线占用 6px 的纵向空间;
  • 线条位置:第i条线(从 0 开始)绘制在纵向i * 6 + 3处,正好落在每个 6px 条带的垂直居中位置;
  • 横向贯穿:每条线从 x=0 画到 x=容器宽度,因此分隔线始终占满可用宽度;
  • 画布宽度自适应:宽度来自getBoundingClientRect(),所以无论容器如何变化,线条都会重新铺满。

抖动效果来自 Rough.js

line()函数在 src/wired-lib.ts#L100-L102 中定义,它调用 Rough.js 的roughLine渲染器生成 SVG path。Rough.js 的随机参数在 src/wired-lib.ts#L32-L54 统一配置:

  • roughness: 1(抖动程度)
  • bowing: 0.85(弯曲程度)
  • maxRandomnessOffset: 2(随机偏移上限)
  • stroke: '#000'strokeWidth: 1.5(作为绘制参数,最终呈色仍受currentColor影响)

正因为 Rough.js 引入了随机偏移,画出的线不是笔直的标准线段,而是带着轻微毛边与弯曲的"手绘感"线条。

随机种子:每次都是独一无二的"手写体"

每个组件实例在创建时都会生成一个随机种子(src/wired-base.ts#L38):

protected seed = Math.floor(Math.random() * 2 ** 31);

这个seed在每次调用line()时作为参数传入 Rough.js。结果是:同一个页面上多个wired-divider的抖动形态各不相同,如同每个人手写的线都不一样;而同一实例的多次重绘(如窗口缩放)则会复用同一个 seed,保持线条形态稳定一致。

响应式重绘机制

WiredDivider通过两种机制监听尺寸变化(src/wired-divider.ts#L52-L72):

  1. 优先使用ResizeObserver观察宿主元素,尺寸变化时触发wiredRender()
  2. 若环境不支持ResizeObserver,则回退到监听windowresize事件(passive: true)。

wiredRender()内部还会做一次尺寸比对(src/wired-base.ts#L45-L58):只有宽高真正变化时才清空旧的 SVG 子节点并按新尺寸重绘,否则直接跳过,避免无意义的重复绘制开销。组件在disconnectedCallback中会正确解除监听,防止内存泄漏。

与整个组件库的关系

wired-divider只是 wired-elements 中 20 余个手绘组件之一,总入口 src/wired-elements.ts 会导出全部组件(包括WiredButtonWiredInputWiredCard等),因此安装一个包即可按需引用任意组件。各组件共享 WiredBase 基类 的"尺寸变化重绘 + 随机种子 + currentColor 上色"这套统一机制,使用体验一致。更多组件的 API 文档位于 docs 目录,组件行为也遵循 README 中的通用安装与使用约定(见 README.md)。

许可证

wired-divider 随 wired-elements 整体以 MIT License 开源,版权归 Preet Shihn 所有(见 docs/wired-divider.md 与 package.json 中的 license 字段)。你可以在个人与商业项目中自由使用,包括 wireframe 工具、原型演示和产品 UI。

  • 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),仅供参考

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

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

立即咨询