返回资讯中心
外部精选
软件工程
#开发者实时流

Drawably 推出可随机生成手绘动画 UI 控件的开源库

来源标题:Drawably creates animated, hand-drawn UI controls with randomized sketches

Drawably 是一个零依赖的 JS 库,可为按钮等 UI 控件生成手绘笔触动画,每次挂载都会基于种子随机数重新绘制草图。压缩后 JS 约 9 KB、样式表 3 KB,并提供 React 封装。按钮支持 outline、solid、scribble 三种样式及 loading、error、success 等异步状态。

Hacker News 最新javatuts2026/11/7 · 7 天前15 分钟阅读

以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。

手绘风格的 UI 控件。每次挂载都会基于种子随机数生成一幅新的钢笔素描,线条会像动画涂鸦一样“沸腾”抖动。零依赖,gzip 后约 9 KB 的 JS(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 variant="solid" onClick={submit}>Done</DrawablyButton>

每次调用 attach 都会返回一个 sketch 句柄:

const sketch = drawablyButton(el); sketch.resketch(); // 用新的随机种子重绘 sketch.resketch(42); // 用指定种子重绘 sketch.destroy(); // 移除 SVG 和所有监听器

按钮

有三种变体:outline(默认)、solid、scribble。按钮还带有一个用于异步工作的状态机:

const button = drawablyButton(el); button.setState("loading"); // 按钮变暗,抖动更快 button.setState("error"); // 以红色重绘 button.setState("success"); // 以绿色重绘 button.setState("idle");

在 React 中,传入 state prop;素描保持不动,只有状态改变:

<DrawablyButton state={saving ? "loading" : "idle"}>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。

像用笔一样为文字添加标注。每个标注都会附着到一个行内元素上,并且不影响其布局;请将它们用在单词或短语上。

import { DrawablyUnderline, DrawablyHighlight, DrawablyCircle, DrawablyArrow } from "drawably/react";

<p> <DrawablyUnderline>Hand-drawn</DrawablyUnderline> UI, a{" "} <DrawablyHighlight>fresh sketch</DrawablyHighlight> on{" "} <DrawablyCircle>every mount</DrawablyCircle>. </p> <DrawablyArrow from={noteRef} to={buttonRef} />

如果装饰跨越了多行换行,每一行都会得到一幅独立的绘制。箭头的 SVG 会以文档坐标附加到 <body> 上,并在窗口大小改变时重绘。位于滚动容器内的锚点会随着滚动而发生偏移。

用上述控件搭建的组件。一个种子就能复现组件中的每一笔;destroy() 会将它们全部拆除。

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>

选项

所有控件都接受相同的基础选项:

颜色是普通的 CSS 自定义属性,所以主题可以一次性设置它们:

:root { --drawably-stroke: #1a1a1a; --drawably-fill: #1a1a1a;

如果页面已加载 Inter,字体就是 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。

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →