Drawably: 手绘风格 UI 控件
摘要
Drawably 是一个 JavaScript 库,为网页开发提供带有动画笔刷效果的手绘风格 UI 控件。
查看缓存全文
缓存时间: 2026/09/14 14:49
Danilaa1/drawably
源码:https://github.com/Danilaa1/drawably
drawably
手绘风格的 UI 控件。每次挂载都会根据种子随机性生成全新的笔触草图,笔画会像动画涂鸦般动态沸腾。零依赖,压缩后约 9 KB JavaScript(React 封装包增加不到 1 KB),外加 3 KB 样式表。可选的手绘字体单独提供(31 KB)。
按钮、复选框、单选按钮和开关均采用沸腾笔触绘制。
安装
npm i drawably
快速开始
import { drawablyButton } from "drawably";
import "drawably/style.css";
drawablyButton(document.querySelector("#done"), { variant: "solid" });
React 版本:
import { DrawablyButton } from "drawably/react";
import "drawably/style.css";
<DrawablyButton>Done</DrawablyButton>
每次调用 attach 都会返回一个草图句柄:
const sketch = drawablyButton(el);
sketch.resketch(); // 使用新种子重新绘制
sketch.resketch(42); // 使用特定种子重新绘制
sketch.destroy(); // 移除 SVG 及所有事件监听器
按钮
三种变体:outline(默认)、solid、scribble。按钮还内置状态机用于异步操作:
按钮的四种状态:idle、loading、error、success
const button = drawablyButton(el);
button.setState("loading"); // 按钮变暗,沸腾加速
button.setState("error"); // 用红色重绘
button.setState("success"); // 用绿色重绘
button.setState("idle");
在 React 中,通过 state 属性传递状态;草图保持不变,仅状态更新:
<DrawablyButton state="success">Save</DrawablyButton>
可通过 --drawably-error 和 --drawably-success 自定义状态颜色。对于次要或危险操作,可设置 tone: "neutral"(暖灰色)或 tone: "danger"(红色)。
控件列表
| 函数 | 期望的元素 |
|---|---|
drawablyButton(el, opts) | 一个 <button> |
drawablyCheckbox(el, opts) | 包含 <input type="checkbox"> 的包裹容器 |
drawablyRadio(el, opts) | 包含 <input type="radio"> 的包裹容器 |
drawablyToggle(el, opts) | 包含 <input type="checkbox"> 的包裹容器 |
drawablyInput(el, opts) | 包含 <input> 的包裹容器 |
drawablyTextarea(el, opts) | 包含 <textarea> 的包裹容器 |
drawablySelect(el, opts) | 包含 <select> 的包裹容器 |
drawablyDivider(el, opts) | <hr> 或 div 元素 |
drawablyCard(el, opts) | 任何块级元素 |
drawablyBadge(el, opts) | 任何内联元素 |
drawablyList(el, opts) | <ul> 或 <ol>;每个 <li> 都会生成草图标记 |
徽章支持 variant: "outline" | "scribble";列表支持 marker: "dash" | "check"。
选择框会保留最宽选项的宽度,确保选择时不会改变布局;在 Chromium 中,选项列表会获得草图边框和笔触勾选标记(使用 appearance: base-select),Safari 和 Firefox 保持系统原生弹窗。实际输入元素保留在 DOM 中,因此键盘操作、表单、标签和屏幕阅读器照常工作。草图是位于底层的 aria-hidden SVG。
所有控件在 drawably/react 中都有对应的 React 版本:
DrawablyButton、DrawablyCheckbox、DrawablyRadio、DrawablyToggle、DrawablyInput、DrawablyTextarea、DrawablySelect、DrawablyDivider、DrawablyCard、DrawablyBadge、DrawablyList。
文本装饰
像用笔一样为文本添加注释。每个装饰都应用于内联元素,不影响布局;适用于单词或短语。
| 函数 | 绘制内容 |
|---|---|
drawablyUnderline(el, opts) | 文本下方粗糙线条,悬停时重新绘制 |
drawablyHighlight(el, opts) | 文本背后的记号笔涂抹效果 |
drawablyCircle(el, opts) | 围绕文本的手绘椭圆 |
drawablyArrow(from, to, opts) | 从一个元素指向另一个元素的箭头 |
import { DrawablyUnderline, DrawablyHighlight, DrawablyCircle, DrawablyArrow } from "drawably/react";
<p>
<DrawablyUnderline>手绘</DrawablyUnderline> UI,
<DrawablyHighlight>全新草图</DrawablyHighlight>
<DrawablyCircle>每次挂载</DrawablyCircle>。
</p>
<DrawablyArrow from={noteRef} to={buttonRef} />
跨越多行的装饰会每行绘制一次。箭头的 SVG 以文档坐标追加到 <body>,并在窗口调整大小时重绘。滚动容器内的锚点会随滚动偏移。
组合组件
由上述控件组合而成的部件。统一的 seed 可重现组合中的所有笔画;destroy() 会销毁所有部件。
| 函数 | 标记结构 | 绘制内容 |
|---|---|---|
drawablyChip(el, opts) | <label><span><input type="checkbox"></span> 文本</label> | 围绕芯片的徽章,输入包裹容器的草图方框 |
drawablyTabs(el, opts) | 子元素为各标签页 | 活动标签页的下划线;active 索引或 aria-selected="true";setActive(i) |
drawablyTooltip(tip, target, opts) | 两个元素 | 围绕提示的卡片,指向目标的箭头 |
drawablyAlert(el, opts) | 可选的 [data-tag] 子元素 | 围绕警告的卡片,标签上的徽章 |
drawablySteps(el, opts) | <ol> | 带勾选标记的列表 |
drawablyKbd(el, opts) | <kbd> | 更稳健的徽章 |
drawablyQuote(el, opts) | 第一个子元素,可选 <footer> | 引文线上的高亮,页脚的分隔线 |
drawablyPager(el, opts) | 子 <button> 元素 | 轮廓页码,实心当前页;active 索引或 aria-current;setPage(i) |
import { DrawablyChip, DrawablyTabs, DrawablyTooltip, DrawablyAlert, DrawablySteps, DrawablyKbd, DrawablyQuote, DrawablyPager } from "drawably/react";
<DrawablyChip defaultChecked>pen</DrawablyChip>
<DrawablyTabs active={tab}><span>a</span><span>b</span></DrawablyTabs>
<DrawablyTooltip to={buttonRef}>undo</DrawablyTooltip>
<DrawablyAlert><span data-tag>new</span> Import from Attio lands Friday</DrawablyAlert>
<DrawablySteps><li>record</li><li>label</li></DrawablySteps>
<DrawablyKbd>⌘K</DrawablyKbd>
<DrawablyQuote><span>less, but better</span><footer>Rams</footer></DrawablyQuote>
<DrawablyPager active={page}><button>‹</button><button>1</button><button>›</button></DrawablyPager>
配置选项
所有控件接受相同的基础选项:
| 选项 | 默认值 | 功能说明 |
|---|---|---|
seed | 随机 | 省略则每次挂载生成唯一草图,传入数字则生成可重现的草图 |
roughness | 1 | 基础草图的抖动幅度 |
boil | 0.3 | 帧间像素闪烁量;设为 0 则渲染静态路径 |
stroke、fill、paper | 笔触蓝/白色 | 颜色值,通过 --drawably-* 自定义属性设置 |
width | 2 | 笔触宽度(像素) |
颜色使用标准 CSS 自定义属性,主题可统一设置:
:root {
--drawably-stroke: #1a1a1a;
--drawably-fill: #1a1a1a;
}
字体优先使用页面加载的 Inter,回退至 system-ui。除非主动加载可选字体,否则库不会引入任何字体。
动效
笔触柔和沸腾:基于同一草图生成三帧,在共享基础上微抖动,通过纯 CSS 以 1200ms 周期循环。悬停或按压时会重绘按钮、复选框、单选按钮、开关、下划线和圆圈;按钮悬停时会浮起,按压时会下沉。
prefers-reduced-motion 会将所有元素冻结为静态草图(包括上方演示图片)。
字体(可选)
Drawably Pen 字体采用与笔触相同的笔迹:包含 a–z、A–Z、数字和标点,由库自身的笔触代码(font/)构建为 31 KB TrueType 字体。它不会通过 style.css 加载;库本身不依赖该字体。若希望标签与界面风格一致:
import "drawably/font.css";
.drawably-button {
font-family: "Drawably Pen", Inter, sans-serif;
}
构建自定义形状
粗糙渲染器已导出。每个函数返回 SVG 路径字符串,variants 函数生成沸腾帧序列:
import { roughRoundedRect, roughLine, roughCircle, variants } from "drawably";
const frames = variants(
(o) => roughRoundedRect(0, 0, 200, 100, 12, o),
{ seed: 7, roughness: 1, boil: 0.3 },
); // 三个路径字符串——渲染它们并循环透明度
同时导出:roughEllipse、roughArrow、roughCheckmark、scribbleFill,以及带种子的 PRNG mulberry32 和 randomSeed。
许可证
MIT。
相似文章
Drawsy
Drawsy 是一个集成画布与代码的 AI 工作空间,用于创意和开发任务。
DayuanJiang/next-ai-draw-io
Next AI Draw.io 是一个开源的 Next.js 网页应用,它集成了 AI 能力与 draw.io,通过自然语言命令来创建和修改图表。该应用支持多个大型语言模型提供商,并提供在线演示。
@dabit3: Canvas UI 的简洁、有趣和美观让我惊叹!我的个人作品集网站就是一个活生生的实验,所以今天让我……
Nader Dabit 分享了他对 Canvas UI 的兴奋之情,这是一个新的开源组件库,可在 Canvas 中渲染 HTML,实现实时着色器与交互式 UI 的结合。他利用 DevinAI 将他的个人作品集变成了一个 Canvas UI 游乐场。
@superalesha: 你的编码代理现在可以制作这样的动画了。我发布了在JavaScript中绘制它们的技能,带有4种视觉…
作者发布了一个JavaScript技能,供编码代理创建手绘风格动画,具有四种视觉风格,可在GitHub上获取,包含示例和渲染工具。
Canvas 中的 HTML 演示
来自 Chrome DevRel 团队的 CSS 和 Web UI 演示合集,包括 Canvas 中的 HTML 演示。