关于 illustration-ink-sketch
面向设计与编码任务的插画技能,指导创作手绘墨线风格的卡通生物或物件。角色常以身体形态承载编码隐喻,并配有晃动笔触、松散色彩和夸张眼睛。
主要功能
- 01手绘笔刷轮廓
- 02松散的标记色块
- 03大眼睛与表情细节
- 04编码主题角色与道具
- 05可复现的 SVG 生成工具
适用场景
- 01绘制加载状态主题图
- 02表现错误或冲突场景
- 03制作产品问候插画
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mengto/ skills --skill illustration-ink-sketch安装后在 Claude Code 中输入/illustration-ink-sketch确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
style.jsonagents/openai.yamldemo/PROMPT.mdexamples/hello-world.svgmerge-conflict.svgslow-build.svgreferences/craft.mdscripts/ink-kit.mjslint.mjsrender.mjs详细介绍
插画:墨线速写
潦草的手绘墨线卡通:一只眼睛圆滚滚的小动物或物件(蜗牛、一对蠕虫、一盆仙人掌),身体承载着编程隐喻;用摇曳的笔刷笔画在随意涂抹的马克笔色块上。适合轻松俏皮的产品场景:加载、错误、问候、冲突。
界限:人物使用干净、均匀的约 2 px 墨线轮廓并填充纯色时,使用 illustration-outlined-cartoon。人物使用黑色笔刷马克笔并搭配一种薄荷色时,使用 illustration-two-colour-brush。动物柔和温馨、不勾勒轮廓且带有颗粒质感时,使用 illustration-grainy-gouache。
本文提取自一组包含 14 种风格的 42 张原创设计与编码卡片;examples/ 中的三张卡片是此风格已发布的卡片。
一句话概括风格
每条边缘都是填充的黑色笔刷多边形,宽度从约 1 px 膨胀到 4.4 px,再逐渐收尖;用几笔彼此分开的笔画绘制在色块上,色块会超出轮廓 2 到 3.6 px;深色区域用排线表现,不直接填满。
配色
根据示例测量得出。每个颜色系列依次为基础色/深色/浅色;深色用于斑驳纹理和马克笔笔迹,浅色用于一道受光笔迹。
| 角色 | 十六进制值 | 用途 |
|---|---|---|
| 卡片背景 | #ffffff(不绘制) | 不要绘制背景矩形。 |
| 墨线 | #1d1a18 | 所有轮廓、排线、头发、斑点、瞳孔、文字。切勿使用纯黑色。 |
| 红色 | #f0563d / #b8311f / #ff8a6a,深色 #9a2414 | 壳、花盆、一只蠕虫 |
| 黄色 | #ffc83d / #ef9a24 / #fff1b0 | 蜗牛身体、爆发状火花、花心;#fff1b0 也用于气泡阴影 |
| 绿色 | #5cb946 / #2f8a38 / #a6dc62 | 草地色块、仙人掌 |
| 蓝色 | #66cdea / #2a8fbf,水滴 #bfe8f7 | 黏液、马克杯、进度条填充、汗滴 |
| 青绿色 | #2fb5a7 / #167a71 / #8fe3d6,深色 #0f5c55 | 红色角色旁边的另一个角色 |
| 木色 | #eaa85c / #b8722f / #f8d6a0,深色 #8a5320 | 书桌、桌子 |
| 腮红色 | #ff7f86(不透明度 0.5 到 0.75),短线 #d9434f 或 #c8303f | 带有三道短线的脸颊圆斑;舌头 |
| 眼部阴影 | #c9d6ea | 每只眼睛白色部分内的月牙形阴影 |
| 纸张色 | #fffaf0,折痕 #d9d2c4 | 贴纸、碎纸片、标签 |
| 单独使用的颜色 | 嘴巴 #3b1f1a、花瓣 #ff8fa0、土壤 #5b3b2c、咖啡 #6b4330、柔和阴影 #7f97ab(不透明度 0.22) | 一次性细节 |
| 白色 | #ffffff | 眼白、光泽笔触、对话气泡、高光 |
- 主体使用两个颜色系列,地面另加绿色(红色 + 黄色的蜗牛、红色与青绿色的蠕虫、绿色仙人掌 + 红色花盆 + 木色书桌)。第三个系列只用于小道具。
- 切勿使用渐变。仅在斑驳纹理、笔迹、光泽和腮红处使用透明度。面积大于瞳孔的纯黑色区域绝不可出现:应改用排线表现。
线条与填充
所有墨线都是笔刷绘制的填充多边形(工具包中的 stroke()),绝不用 SVG 描边:示例中 stroke 属性的数量为零。
| 标记 | 宽度为 480 时的数值 |
|---|---|
| 轮廓 | OUT(centre):w 4.6、最大 4.4、最小 1.0、压力 0.4(波长 26)、笔尖 0.3,右下方阴影侧更粗(k 0.3 至 0.36)。首尾收尖 t0/t1 为 3 至 30 px。每个形状由 3 至 6 条分离的笔画构成,在左上受光侧留一个缺口,并有一处超出 2 至 14 px |
| 次要线条(边缘、气泡尾巴、嘴部) | w 2.4 至 3.2、笔尖 0.45、压力 0.4、pf 8 |
| 细节(肋条、皱纹、木纹) | w 1.1 至 1.8,墨色不透明度为 0.55 至 0.85 |
| 排线 | w 1.0 至 1.15、间距 2 至 3.2、角度 -48 至 -58 度(右侧受光的形状则镜像为 -130 度),末端在边缘内侧抖动 |
| 毛发 | 每条轮廓笔画 2 至 5 根,w 0.9 至 1.5、长度 2.5 至 7,阴影侧朝外 |
| 墨点 | 每张卡片 9 至 11 个,大小 0.8 至 1.7,放在空白边缘处 |
| 填充 | 基础颜色,偏离轮廓 2 至 3.6 px(fillShape off),控制点抖动 1.2 至 1.6 |
| 斑驳 | 在每块填充上应用斑驳滤镜,使用深色,透明度 0.36 至 0.5 |
| 马克笔笔痕 | 深色,w 4 至 16、透明度 0.3 至 0.55,沿阴影侧画 1 至 3 道,并裁剪到形状内 |
| 高光 | 白色,w 2.4 至 6、透明度 0.85 至 0.95,在圆形物体左上受光处画一到两道笔痕 |
每张卡片使用两个滤镜,且都带有前缀:rough(分形噪声 0.45、位移 0.7)包裹整张卡片,让每条边缘都颤动;mottle(噪声 0.035 x 0.06、3 个八度、alpha 2.6/-1.25)遮罩水彩色块。svgOut 会写入这两个滤镜。
角色
不画人。主角是小动物或有生命的物体(蜗牛、虫子、仙人掌;也可以是杯子、虫子、扫地机器人),其身体本身就是隐喻:蜗牛壳是加载旋转图标,虫子的细尾巴是带有提交点的 git 分支,花盆上贴着一个 </> 贴纸。
- 身体: 沿脊柱绘制管状结构并使用宽度函数(
tube()),或通过 12 至 24 个控制点绘制闭合的团块。管状身体末端鼓起形成头部(身体 40、头部 49)。主体约占卡片宽度的 60% 至 70%。 - 眼睛,标志性特征: 巨大的凸眼,半径 19 至 27.5(直径 40 至 55 px,占头部宽度的 1/3 至 1/2),通常是一对大小略有不同、相互接触或重叠的眼睛。白色圆盘,右下方有
#c9d6ea色阴影弧,眼眶在起笔处鼓起并越过起点;瞳孔半径为眼睛半径的 0.5 倍,朝角色注视方向偏移 4 至 10 px;一个大高光和一个小白色高光。用身体颜色的眼皮(h 0.34 至 0.38,倾斜 17 度)表现不悦。 - 面部: 腮红椭圆为 8 x 4.6,带三道短红线;微笑由一条弯曲笔画和两端的短小笔触组成,欢呼时用填充的深色嘴巴并加粉色舌头,生气时画白色咬紧牙齿的形状并加两条牙缝线,撅嘴时画向下弯曲的笔画和下唇。
- 手部,需要时再为角色添加: 使用四肢颜色;手掌和 3 根手指加一根拇指均为胶囊形,指关节处不闭合描边,并添加一道指关节皱纹和阴影侧的深色笔痕(工具包中的
hand())。大小参照仙人掌:在缩放 1.25 时,手指长 12 至 14.5。拇指的位置按references/craft.md中的左右手惯用性表格设置。 - 触角、茎、尾巴: 使用两侧带轮廓的渐细管状结构,或单条螺旋笔刷笔画。若使用细腿,则用单条宽 2.4 至 3 的笔刷笔画,末端画小的填充椭圆。
- 短腿和爪子(小型哺乳动物): 使用
shadeTube+limbOutline({ under: bodyLoop })绘制宽 9 至 14 的短管状结构,末端画一个 5.4 x 3.2 的花瓣粉色椭圆,并在下方加一道短墨线。先画远侧的一对,再画身体,最后画近侧的一对;后腿朝臀部后方倾斜,前腿朝肩部前方倾斜,让角色看起来在奔跑。 - 用力与情绪标记: 汗滴、由四个弯曲括号组成的愤怒标记、运动弧线(两条同心笔画,宽度分别为 2.4 和 1.8,逐渐收尖)、蒸汽卷曲线。
装饰与道具
- 地面(任选其一): 一片参差的草地,范围从 x 52 延伸到 428,顶边位于 y 272 到 280,底边向上弯起,两端带有干刷笔触、草叶飞笔和一两簇高高的草丛(
grassPatch、tuft);或者一张略微俯视的木桌桌面,带有木纹线条和一条排线桌腿。 - 接触阴影: 每个着地部位正下方画一个排线椭圆(w 1.0,sp 3 到 3.2,op 0.85 到 0.9),再在身体底部加一道颜色更深的笔触。道具必须有所依托:标牌要插在草地里的木桩上,杯子要放在桌面上。只有气泡、飞散纸屑和标记可以悬浮。
- 道具,1 到 3 件: 一个
</>代码贴纸、一个带手写文字的对话气泡、一张带代码尖括号的撕纸、一只冒着蒸汽卷曲线条的杯子、一条进度条(白色轨道、蓝色排线填充、手写百分比)、提交圆点、爆裂星形、一朵花。 - 文字: 用笔刷笔触手写(w 2.4 到 2.6,字形高 12 到 17 px),绝不使用字体。
- 切勿使用: 没有笔刷轮廓的规整几何矢量形状、渐变、投影、纯黑填充、背景场景、人物。
构图
- 不要背景矩形。所有内容都须位于 x 36 到 444、y 40 到 340 之内;只有干刷地面两端可以延伸到 x 36。
- 主体位于地面中央偏下,宽度约占卡片的 60% 到 70%,高度约占 60%;眼睛是上半部分的视觉焦点。用一个气泡、纸屑或标记填补最空旷的上方角落。圆形或高挑的主体(如轮子、花盆)宽度只占约 50%;在一侧加一个落地道具填满画面宽度。
- 本技能中的所有数值均以卡片尺寸为准。在缩放组内部,按「数值 / scale」绘制,并先在卡片坐标中确定接触点(如爪子放在边缘上、花盆放在桌面上),再将其映射到组内坐标:
const G = w => [B0[0] + (w[0] - B0[0]) / S, B0[1] + (w[1] - B0[1]) / S]。 - 在设计尺寸下,于地面接触点周围使用
<g transform="translate(cx gy) scale(1.12) translate(-cx -gy)">绘制主体,并设置setWScale(1/1.12),使线条宽度保持不变,之后再设置setWScale(1)。 svgOut(file, {shift: [0, -8]})可移动整张卡片,以平衡边距。
技法
scripts/ink-kit.mjs 是生成三个示例所用的带种子的笔刷库:将其放到原文件的位置运行,Merge Conflict 和 Hello World 生成器会精确复现已发布卡片中的每一笔(唯一差异是旧文件写入器在 <defs> 中留下了一个多余的文本节点)。每一笔都使用一个具有独立随机序列的字符串键,因此编辑或添加一笔都不会打乱其余笔画;更改某个键即可重新生成单独的一笔。
// card.mjs, next to a copy of ink-kit.mjs
import * as K from './ink-kit.mjs';
const { PAL, OUT } = K;
K.setPrefix('xx-'); // every id: filters and clip paths
K.grassPatch('grass', { x0: 60, x1: 420, top: 276, bot: 320 });
K.hatch('cast', K.arc(240, 286, 70, 6, 0, 360, 10), { ang: -58, sp: 3, w: 1.0, inset: 1.2, over: 0.4, op: 0.9 });
const ctrl = []; // a round body: control points
for (let a = 0; a < 360; a += 30) ctrl.push([240 + 70 * Math.cos(a * K.deg), 220 + 62 * Math.sin(a * K.deg)]);
const loop = K.fillShape('body', ctrl, PAL.red.c, { off: [-3, 2], j: 1.4 }); // slipped fill
K.mottle(loop, PAL.red.d, 0.46);
K.clipOpen(loop); // shading stays inside
K.stroke('streak', K.arc(240, 220, 60, 54, -10, 120, 6), { w: 13, t0: 20, t1: 25, wob: 1.5 }, PAL.red.d, 0.45);
K.stroke('gloss', K.arc(240, 220, 52, 48, 198, 236, 4), { w: 6, t0: 8, t1: 12 }, '#ffffff', 0.95);
K.clipClose();
const dl = K.resample(K.catmull(ctrl, true, 10), 1); // outline in two strokes: gap top-left, overshoot
const o1 = K.stroke('o1', K.loopSlice(dl, 0.62, 1.18), { ...OUT([240, 220]), dense: true, t0: 12, t1: 10 });
K.stroke('o2', K.ext(K.loopSlice(dl, 0.2, 0.58), 3), { ...OUT([240, 220]), dense: true, t0: 8, t1: 6 });
K.hairs('hairs', o1.C, 4, { c: [240, 220] });
K.eye('eyeL', [216, 172], 21, [223, 176], 10.5);
K.eye('eyeR', [262, 168], 23, [269, 173], 11.5);
K.blush('blL', 206, 222); K.blush('blR', 280, 218);
[[96, 120, 1.5], [400, 96, 1.2], [420, 230, 1.1]].forEach((p, i) => K.fleck('fk' + i, ...p));
K.svgOut(new URL('./card.svg', import.meta.url).pathname, { label: 'Title, Ink sketch style' }); // next to card.mjs
工具包中的组件:stroke(笔刷)、OUT(c)(轮廓预设)、hatch、fillShape、mottle、clipOpen/clipClose、hairs、fleck、dry、eye(带 lid)、tube 以及 across/acrossLine、shadeTube(为肢体或身体添加填充、斑驳、笔触、高光和肋纹)、limbOutline(只为肢体位于身体外的部分上墨)、hand、grassPatch、blade/tuft、blush、sweatDrop、codeSticker、paperScrap(撕纸便笺;返回用于在其上书写的 T(x, y))、fillRing(正面朝向的轮子或甜甜圈表面:使用奇偶填充和斑驳效果)、arc、loopSlice、ext、runsOutside、setWScale、svgOut。
用一笔刷笔触绘制的符号示例,例如无穷符号(26 px 宽的莱姆尼斯卡特曲线):
const inf = []; for (let t = 0; t <= 2 * Math.PI + 0.35; t += 0.07) { const s = Math.sin(t), c = Math.cos(t), d = 1 + s * s; inf.push([x + 13 * c / d, y + 13 * s * c / d]); }
K.stroke('inf', inf, { w: 3, t0: 3, t1: 5, nib: 0.45, press: 0.3, pf: 8 });
node scripts/ink-kit.mjs --demo $W/demo.svg 会写入演示卡片并打印 wrote <path> 203.5KB 316 elements:一个红色、长着凸眼、在草地上挥舞三指之手的 blob,带有排线、代码贴纸、汗滴和飞散笔点。使用 --prefix ikd- 运行 lint 检查可通过。
常见问题
- 剪贴画式线条。 笔画宽度一致,或使用 SVG
stroke属性,都会显得像矢量图。每条线都应是带有粗细变化和压力感的笔刷多边形。 - 封闭的轮廓。 用一个闭合环绕住形状会显得像描摹。将每条轮廓拆成 3 到 6 笔,在受光一侧留一道缺口,并让其中一笔稍微越过轮廓。
- 填色紧贴线条。 看起来会像涂色书里的矢量图。让每块填色错开 2 到 3.6 px,每个形状的错开方向都不同。
- 平涂色彩。 没有斑驳、条纹或光泽的填色会显得像印刷品。每块填色都要有斑驳,圆形物体上还要至少加一道深色条纹和一道高光笔触。
- 纯黑阴影。 用排线处理;通过
keep让排线密度朝光源方向逐渐变淡。 - 呆滞的凸眼。 瞳孔居中,或缺少高光,都会让目光显得呆滞。将瞳孔朝动作方向偏移 4 到 10 px;始终加两个白点。
- 到处都是毛发。 每笔画的毛发超过 5 根,线条就会显得毛茸茸。控制在 2 到 5 根,并放在背光侧。
- 主体悬浮。 没有排线接触椭圆和深色底部条纹,角色看起来就会悬在草地上方。
- 缩放组中的线宽不一致。 忘记设置
setWScale(1/s)会让每条线都粗 12%。 - 一次编辑后所有内容都变了。 复用或重新编号键会重新生成标记;为每个标记指定稳定且唯一的键。
- 同一页面上的两张卡片共用一个滤镜。 为每张卡片调用
setPrefix并指定唯一前缀;使用--prefix运行 lint。 - 排版文字。 对话气泡中的字体会破坏手绘感。用笔刷笔画书写;如果画出字形比笑点本身还费时,就用标记来表达(计数线、V 形符号、图标)。
- 脸紧挨着道具。 测试中,仓鼠的鼻子、嘴巴和爪子会与滚轮边缘融在一起。脸周围留出 25 px 的空隙,肢体末端也不要接到嘴边。
- 标记超出纸张。 计数点溢出纸片的撕边后,会像多余的墨迹。在纸片边缘内至少留出 4 px。
- 悬空的标牌。 主体旁边悬在空中的便条看起来像画错了;用带排线的接触阴影将它固定在支杆上。
示例
slow-build.svg(缓慢构建):一只满头大汗的蜗牛趴在 27% 进度条上,壳是带排线彗星扫尾的螺旋加载图标;展示眼柄、代码贴纸、草地、手写的 % 和柔和的接触阴影。merge-conflict.svg(合并冲突):一只红色虫子和一只青绿色虫子在分支线交汇处迎头相撞;展示管状身体和分节环、皱眉的眼睑、咬紧的牙齿和撅起的嘴、提交点、爆裂火花、愤怒标记,以及一张被甩出去的<<<<纸片。hello-world.svg(Hello World):桌上的盆栽仙人掌挥手说“你好,世界”,旁边放着咖啡杯;展示拇指方向正确的三指挥手、搭在花盆边缘上的手臂、带有刺状短线的棱纹、木纹、弯曲的蒸汽,以及对话气泡中的手写字。
工作流
从此技能文件夹运行。render.mjs 需要在此处、当前文件夹中或全局安装 playwright-core(或 playwright),并且需要 Chromium。
- 简述。 写明主题和隐喻:是哪种小动物,身体的哪个部位承载这个想法。记下在 480 px 尺寸下最能传达它的一个表情(眼睛、嘴巴、斑纹)。
- 主体与姿势。 在纸上或注释中勾勒轮廓:脊柱、眼睛位置、地面线。每只手都标明左/右和掌心/手背(craft.md)。
- 设置:
W=/tmp/ink-card; mkdir -p $W; cp scripts/ink-kit.mjs $W/,然后根据 Techniques 骨架编写$W/card.mjs,并使用唯一前缀。 - 打底: 只画地面、身体底色和眼睛。渲染并检查尺寸、位置和 1 倍大小下的辨识度。
- 绘制: 添加阴影(斑驳、条纹、排线、高光)、分开绘制的轮廓线、毛发、面部、道具、文字、斑点。
- 渲染:
node $W/card.mjs && node scripts/render.mjs $W/card.svg $W/card.png(card.mjs 会在同一目录下写入 card.svg)。 - 图版:
node scripts/render.mjs --sheet $W/sheet.png examples/*.svg $W/card.svg。问问自己:“画风一致、系列感一致吗?” - 放大眼睛、手、接触点和文字,放大 6 到 8 倍:
node scripts/render.mjs $W/card.svg $W/eyes.png --zoom x,y,w,h --scale 8。 - 根据
references/craft.md中的五项标准和 Verify 列表进行审阅;通过编辑生成器来修正(绝不修改 SVG);然后重复操作。预计需要三到四轮。 - Lint 检查:
node scripts/lint.mjs $W/card.svg --prefix xx- --palette style.json。修复每个 ERROR;每种 WARN 颜色都必须是刻意为之的单次使用。
验证
- 任何地方都没有
stroke=属性;每条线都是填充为#1d1a18的笔刷多边形。 - 轮廓线有粗细变化且两端渐细(细处约 1 px,粗处约 4.4 px),每个形状的轮廓线都有断口和延伸段。
- 每块填色都明显越过线条 2 到 3.6 px。
- 每块填色都有可见的斑驳;圆形部分带有深色条纹和白色高光笔触。
- 阴影和深色区域采用 -48 到 -58 度的排线,间距为 2 到 3.2;没有纯黑区域。
- 眼球直径为 40 到 55 px,瞳孔偏向动作方向,每只眼睛有两个高光点,内部带有阴影弧。
- 每段轮廓线有 2 到 5 根毛发;边缘留白处有 9 到 11 个斑点。
- 主体置于一片草地或桌面上,并带有排线接触阴影;没有东西意外悬空。
- 主体使用两组颜色;地面使用绿色或木色;仅使用调色板中的十六进制颜色。
- 整张卡片都包在带前缀的
rough滤镜中;所有 id 都带有前缀。 - 文字由笔刷绘制,而非使用字体。
- 不看标题,在 1 倍大小下也能看懂隐喻。