图形学【免费下载链接】roughCreate graphics with a hand-drawn, sketchy, appearance项目地址https://gitcode.com/gh_mirrors/ro/rough点击查看免费下载导读Rough.js 是一个用 HTML Canvas 与 SVG 绘制手绘草图风格图形的轻量级图形库核心库体积小于 9 kB其 CHANGELOG.md 完整记录了 4.0.0 至 4.5.0 版本间的关键功能迭代与缺陷修复。本文将逐条拆解该变更日志结合 src/ 目录 下的源码实现带你理解每个版本特性背后的算法原理、对应配置参数Options以及这些改动在 Canvas / SVG 两种渲染管线中的落地方式读完即可掌握 Rough.js 渲染系统的核心机制并正确使用 4.x 新增的种子随机、虚线、精度控制等能力。版本脉络总览Rough.js 4.x 系列含 3.1.0 的填充样式基础的版本演进可以归纳为四个主题版本发布时间核心主题3.1.02019-03-14新增 sunburst、dashed、zigzag-line 三种填充样式及配套参数4.0.02020-01-13随机种子、扫描线 Hachure 算法重写、无描边模式、移除 sunburst4.1.02020-01-13非 SVG 曲线支持填充4.3.02020-05-11虚线绘制参数、关闭双重描边效果4.4.02021-05-09preserveVertices 顶点保持4.5.02021-05-09嵌套路径算法改进、zigzag 填充优化、小数点精度配置其中 4.0.0 是一次架构级重写随机数生成器被种子化Hachure 填充算法从自绘改为基于第三方hachure-fill库的扫描线实现见 package.json 的依赖声明这也是后续多个填充样式共享扫描线基础设施的开端。3.1.0三种填充样式与三个填充参数新增填充样式3.1.0 版本引入三种新的填充样式sunburst、dashed和zigzag-line并与已有的hachure、solid、zigzag、cross-hatch、dots共同构成填充样式体系。该版本同时为这些样式新增了三个 Options 属性dashOffsetdashed 填充中每一段虚线的长度dashGapdashed 填充中虚线之间的间隙长度zigzagOffset使用 zigzag-line 填充时单个锯齿三角形的宽度。需要说明的是sunburst 样式在后来的 4.0.0 中被移除见下文因此当前仓库中实际可用的填充样式为 README 中列出的 7 种hachure默认、solid、zigzag、cross-hatch、dots、dashed、zigzag-line。样式到实现类的映射集中在 src/fillers/filler.ts 的getFiller工厂函数中。dashed 填充的源码实现dashed 与 zigzag-line 填充的核心实现均在 src/fillers/dashed-filler.ts 与 src/fillers/zigzag-line-filler.ts 中。dashed 填充首先复用扫描线算法得到一组平行线然后对每条线按dashOffset dashGap的周期切分成虚线段const offset o.dashOffset 0 ? (o.hachureGap 0 ? (o.strokeWidth * 4) : o.hachureGap) : o.dashOffset; const gap o.dashGap 0 ? (o.hachureGap 0 ? (o.strokeWidth * 4) : o.hachureGap) : o.dashGap;从源码可以推断dashOffset与dashGap的默认值均为-1当未显式设置时二者会回退到hachureGap若hachureGap也为-1则最终回退到strokeWidth * 4。这与 src/generator.ts 中默认选项dashOffset: -1, dashGap: -1, hachureGap: -1的定义一致即负值即自动推导的参数约定。zigzag-line 填充的源码实现zigzag-line 填充同样基于扫描线但每条线的锯齿宽度由zigzagOffset决定默认值-1回退到hachureGap再回退到strokeWidth * 4并通过count length / (2 * zo)计算锯齿数量逐段生成两个斜向线段组成一个锯齿单元见 src/fillers/zigzag-line-filler.ts。填充样式工厂所有填充样式通过 src/fillers/filler.ts 中的getFiller(o, helper)按o.fillStyle懒加载实例化默认回退为hachure。因此使用示例const rc rough.canvas(document.getElementById(canvas)); // dashed 填充 rc.rectangle(10, 10, 100, 100, { fill: red, fillStyle: dashed, dashOffset: 8, // 每段虚线长度 dashGap: 4, // 虚线间隙 }); // zigzag-line 填充 rc.polygon([[10, 130], [110, 130], [60, 200]], { fill: blue, fillStyle: zigzag-line, zigzagOffset: 6, // 锯齿三角形宽度 });4.0.0架构级重写的五个关键改动1. 可选随机种子seed4.0.0 新增了seed选项用于保证相同参数必然生成相同向量。其底层实现在 src/math.ts 中export class Random { constructor(seed: number) { this.seed seed; } next(): number { if (this.seed) { return ((2 ** 31 - 1) (this.seed Math.imul(48271, this.seed))) / 2 ** 31; } else { return Math.random(); } } }seed缺省为0时使用Math.random()每次绘制都不同给定非零seed时采用线性同余生成器乘数 48271产生确定性随机序列从而让同一参数下的图形每次渲染完全一致。Random实例通过 src/renderer.ts 的random(ops)惰性挂载到ResolvedOptions.randomizer上所有粗糙度偏移_offset/_offsetOpt都经由该随机源计算。使用方式const rc rough.svg(svg); const opts { seed: 42 }; const node rc.rectangle(10, 10, 200, 200, opts); // 两次调用结果一致 svg.appendChild(node);此外 API 提供rough.newSeed()对应 src/rough.ts 的RoughGenerator.newSeed()获取一个合法的随机种子值const seed rough.newSeed(); // 返回 0 ~ 2^31-1 之间的整数注意disableMultiStroke开启后渲染会复制并修改种子cloneOptionsAlterSeed在 src/renderer.ts 中让第二层描边seed 1以保证内外两层轨迹不同。2. 基于扫描线的 Hachure 新算法4.0.0 将 Hachure 填充算法重写为扫描线scanline算法代码体积更小且约提速 20%。该算法封装在依赖库hachure-fill中Rough.js 侧的入口是 src/fillers/scan-line-hachure.tsexport function polygonHachureLines(polygonList: Point[][], o: ResolvedOptions): Line[] { const angle o.hachureAngle 90; // 默认 -41即实际 49 度 let gap o.hachureGap; if (gap 0) { gap o.strokeWidth * 4; } gap Math.round(Math.max(gap, 0.1)); // roughness 1 时以 30% 概率跳行形成更手绘的疏密不均 let skipOffset 1; if (o.roughness 1) { if ((o.randomizer?.next() || Math.random()) 0.7) { skipOffset gap; } } return hachureLines(polygonList, gap, angle, skipOffset || 1); }关键实现细节角度hachureAngle默认-41见 src/generator.ts加 90 后传入扫描线算法因此源码中实际填充角度为 49 度间距hachureGap默认-1负值时回退为strokeWidth * 4并保证不小于0.1随机跳行当roughness 1时有约 30% 概率让相邻扫描线间隔拉大一倍产生自然的笔触疏密变化。3. 根据形状尺寸自适应随机度与曲线步数4.0.0 调整了随机偏移与曲线步数的计算逻辑使其随图形尺寸变化。证据位于 src/renderer.ts椭圆/圆形的步数generateEllipseParams中stepCount Math.ceil(Math.max(o.curveStepCount, (o.curveStepCount / Math.sqrt(200)) * psq))src/renderer.tspsq 与形状的宽高平方根成正比即大椭圆自动加密采样点直线的粗糙度增益_line中当线段长度 200时roughnessGain 1 500时降至0.4中间长度线性插值src/renderer.ts即长线段的抖动幅度按比例收敛避免手绘抖动被过度放大。4. 移除 async/worker 构建变更日志明确指出 async/worker 构建已移除——并发渲染可以在应用层自行实现因此不再内置在库中。当前 package.json 的构建产物对应 README 所述三种格式CommonJSbundled/rough.cjs.js、ESMbundled/rough.esm.js、浏览器 IIFEbundled/rough.js。5. 支持无描边绘制stroke: nonestroke: none将不再生成轮廓向量。实现依据src/generator.ts 中rectangle等形状方法以o.stroke ! NOSNOS 即none为条件决定是否压入轮廓 OpSetsrc/canvas.ts 将stroke none映射为transparent避免 Canvas 因空值抛错全局常量NOS none定义于 src/generator.ts。因此可以只画填充不画描边rc.rectangle(10, 10, 100, 100, { fill: rgba(255,0,0,0.4), stroke: none, // 只保留填充 });另外4.0.0 移除了sunburst填充样式原因是该样式存在大量边界情形无法正确处理且使用率不高这与 3.1.0 的引入形成闭环。4.1.0非 SVG 曲线的填充能力4.1.0 为curve()添加了填充支持。此前只有 SVG path 可被填充现在通过 src/generator.ts 的curve(points, options)实现solid 填充以disableMultiStroke: true并叠加fillShapeRoughnessGain默认 0.8重新拟合曲线作为填充路径src/generator.ts并通过_mergedShape过滤中间move指令形成单一闭合路径非 solid 填充hachure 等图案先将曲线控制点转换为贝塞尔采样点依赖points-on-curve的curveToBezier与pointsOnBezierCurves得到多边形点集后调用patternFillPolygonssrc/generator.ts。因此可以这样为贝塞尔曲线填充const rc rough.canvas(canvas); rc.curve([[30, 30], [90, 120], [150, 30], [210, 120]], { fill: red, fillStyle: cross-hatch, });4.3.0虚线描边、关闭双重描边与 SVG 填充规则修复新增四个虚线相关选项4.3.0 新增以下 Options分别控制轮廓与填充线的虚线选项作用使用位置strokeLineDash轮廓虚线数组映射到 SVGstroke-dasharray/ CanvassetLineDashsrc/svg.ts、src/canvas.tsstrokeLineDashOffset轮廓虚线偏移SVGstroke-dashoffset/ CanvaslineDashOffsetsrc/svg.ts、src/canvas.tsfillLineDash填充扫描线的虚线数组src/svg.ts、src/canvas.tsfillLineDashOffset填充扫描线的虚线偏移src/svg.ts、src/canvas.ts两者在渲染管线中的处理方式相同Canvas 通过ctx.setLineDash(...)/ctx.lineDashOffsetSVG 通过stroke-dasharray/stroke-dashoffset属性。注意轮廓虚线作用于fillSketch类型以外的描边路径填充虚线则作用于fillSketch即图案填充生成的扫描线路径。rc.rectangle(10, 10, 200, 200, { stroke: #333, strokeWidth: 2, strokeLineDash: [6, 3], // 6px 实线 3px 空隙 strokeLineDashOffset: 2, // 虚线相位偏移 fill: green, fillStyle: hachure, fillLineDash: [2, 2], });disableMultiStroke 与 disableMultiStrokeFill新增的两个布尔选项用于关闭双重描边效果——Rough.js 的默认美学是同一笔触绘制两层略有偏移的轨迹手绘感来源disableMultiStroke关闭轮廓的第二层描边disableMultiStrokeFill关闭填充线fillSketch的第二层描边。实现逻辑在 src/renderer.ts 的_doubleLinefunction _doubleLine(x1, y1, x2, y2, o, filling false): Op[] { const singleStroke filling ? o.disableMultiStrokeFill : o.disableMultiStroke; const o1 _line(x1, y1, x2, y2, o, true, false); if (singleStroke) { return o1; } const o2 _line(x1, y1, x2, y2, o, true, true); return o1.concat(o2); }第二层描边overlay的抖动幅度较小randomHalf()而非randomFull()且种子会 1见 4.0.0 小节。曲线与椭圆路径同样遵循该开关_bezierTo以o.disableMultiStroke ? 1 : 2决定迭代次数src/renderer.ts椭圆以(!o.disableMultiStroke) (o.roughness ! 0)决定是否追加第二层src/renderer.ts。rc.rectangle(10, 10, 200, 200, { disableMultiStroke: true, // 轮廓只画一层更干净利落 disableMultiStrokeFill: true, // 填充线也只画一层 });SVG 实心填充遵循 evenodd 规则4.3.0 修复了 SVG 实心填充未默认遵守 evenodd 规则的问题。修复位置在 src/svg.tsif (drawable.shape curve || drawable.shape polygon) { path.setAttribute(fill-rule, evenodd); }Canvas 侧也做了对齐在 src/canvas.ts 中curve、polygon、path形状的 fill 使用evenodd规则其余形状用nonzero。这意味着自相交曲线或多边形内部的孔洞将被正确镂空。4.4.0preserveVertices 保持顶点稳定4.4.0 新增preserveVertices选项在渲染形状时不随机化顶点/端点非常适合路径拼接场景——多个相连线段共享端点时如果各自随机偏移端点连接处就会出现断裂开启该选项后连接点保持精确重合。实现要点在 src/renderer.ts 的_line与_bezierTo_line中所有端点坐标均以preserveVertices ? 0 : random...的形式条件化src/renderer.ts_bezierTo中贝塞尔终点f preserveVertices ? [x, y] : [x _offsetOpt(...)]src/renderer.ts。注意preserveVertices仅约束顶点线段中间的抖动bowing 弯曲、控制点偏移仍然保留因此图形不会退化为规整几何手绘感依旧存在。const pts [[50, 50], [150, 50], [150, 150], [50, 150]]; const o { preserveVertices: true }; // 相邻边共享的顶点不会被随机偏移闭合路径连接处严丝合缝 rc.linearPath(pts, o); rc.linearPath([...pts, pts[0]], o);4.5.0嵌套路径、zigzag 改进与精度控制嵌套/相交路径算法优化4.5.0 针对嵌套与相交路径改进了算法对应上游 issue #183同时优化了凹多边形与嵌套路径下的 zigzag 填充质量。从实现结构看所有图案填充都依赖polygonHachureLinessrc/fillers/scan-line-hachure.ts基于hachure-fill库对多多边形列表polygonList: Point[][]整体求扫描线而非逐多边形独立计算这是嵌套路径能够正确填充的基础。zigzag 填充src/fillers/zigzag-filler.ts则对每条扫描线沿hachureAngle方向两侧各偏移半个间距生成两条折线形成锯齿。dots 填充在 roughness 1 时的修复修复了roughness 1时 dots 填充生成怪异形状的问题对应 issue #193。当前 src/fillers/dot-filler.ts 的实现中每个点用fweightfillWeight 负值时取strokeWidth / 2作为直径绘制小椭圆且点位置在gap/4半径范围内做随机散布const ro gap / 4; const cx (x - ro) Math.random() * 2 * ro; const cy (y - ro) Math.random() * 2 * ro; const el this.helper.ellipse(cx, cy, fweight, fweight, o);从源码可以看出dots 填充的点间距由hachureGap控制负值回退strokeWidth * 4点的粗细由fillWeight控制。注意点散布使用Math.random()而非种子随机源因此 dots 样式的点位置不受seed约束这是可以从 src/fillers/dot-filler.ts 推断出的实现细节。新增 fixedDecimalPlaceDigits 精度控制4.5.0 新增fixedDecimalPlaceDigits属性可在Canvas 与 SVG 两种渲染方式下统一配置坐标小数位精度。其作用是在最终输出路径时对每个坐标调用toFixed(fixedDecimalPlaceDigits)从而压缩 SVG path 的d属性体积减少坐标小数位让 Canvas 绘制与 SVG 输出的坐标保持一致便于跨端对拍或测试快照。实现位置Canvas_drawToContext中对item.data逐项d.toFixed(fixedDecimals)src/canvas.ts精度读取自drawable.options.fixedDecimalPlaceDigitssrc/canvas.tsSVGRoughSVG.draw将精度透传给opsToPath(drawing, precision)src/svg.ts最终由RoughGenerator.opsToPath统一执行d.toFixed(fixedDecimals)src/generator.ts对fillSketch图案填充扫描线同样生效src/canvas.ts 与 src/svg.ts。const rc rough.svg(svg); // 输出坐标仅保留 2 位小数SVG path d 属性更紧凑、更稳定 const node rc.rectangle(10, 10, 200, 200, { fixedDecimalPlaceDigits: 2 }); console.log(node.querySelector(path).getAttribute(d));修复Arc 实心填充 180° 断裂修复了圆弧角度超过 180 度时 solid 填充失效的问题。实心圆弧填充路径由 src/generator.ts 生成将o的副本设置为disableMultiStroke: true后重新生成 arc OpSet 并标记为fillPath。修复前大角度弧的闭合端点连接存在偏移导致填充破损修复后的填充路径使用精确端点lineTo到cx, cy与弧的起止点不再叠加随机偏移见 src/renderer.ts 中closed !roughClosure分支。rc.arc(150, 150, 200, 120, 0, Math.PI * 1.5, true, { fill: orange, fillStyle: solid, // 超过 180° 的闭合弧也能正确实心填充 });修复roughness 0 时椭圆无缺口修复了roughness 0时椭圆端点出现缺口notch的问题。对应实现在 src/renderer.ts 的_computeEllipsePoints当o.roughness 0时走coreOnly分支将步长缩小为原来的 1/4 并以精确的三角函数坐标生成闭合采样点同时跳过第二层描边(!o.disableMultiStroke) (o.roughness ! 0)条件在 src/renderer.ts。也就是说 roughness 0 会渲染出无抖动、无缺口的精确椭圆。跨版本参数速查表综合 3.1.0 ~ 4.5.0 全部版本以下 Options 均由 src/core.ts 定义、默认值在 src/generator.ts 中参数默认值引入版本说明seed04.0.0随机种子0 表示完全随机strokeLineDash无4.3.0轮廓虚线数组strokeLineDashOffset无4.3.0轮廓虚线偏移fillLineDash无4.3.0填充线虚线数组fillLineDashOffset无4.3.0填充线虚线偏移disableMultiStrokefalse4.3.0关闭轮廓双层描边disableMultiStrokeFillfalse4.3.0关闭填充线双层描边preserveVerticesfalse4.4.0保持端点不被随机化fixedDecimalPlaceDigits无4.5.0输出坐标小数位精度dashOffset-1自动推导3.1.0dashed 填充虚线长度dashGap-1自动推导3.1.0dashed 填充虚线间隙zigzagOffset-1自动推导3.1.0zigzag-line 锯齿宽度hachureAngle-414.0.0 起填充角度实际 90 使用hachureGap-1自动推导4.0.0 起填充线间距fillShapeRoughnessGain0.84.0.0 起填充形状相对轮廓的粗糙度增量深入验证可视化测试与渲染双管线变更日志中涉及的所有参数都能在仓库的 visual-tests/ 目录找到对应的可视化测试页面包括 Canvas 与 SVG 两套渲染管线的对照visual-tests/canvas/dashed/dashed 填充与虚线描边在 Canvas 下的效果覆盖 arc、curve、ellipse、line、path、polygon、rectangle 等图形visual-tests/canvas/singlestroke/单层描边效果可直接对照disableMultiStroke与disableMultiStrokeFill的作用visual-tests/svg/dashed/同一组虚线能力在 SVG 管线下的表现visual-tests/canvas/curve-seed.html 与 visual-tests/canvas/poly-seed.html种子随机性的可视化验证。这些 HTML 页面展示了两次相同 seed 调用下曲线与多边形轮廓完全重合的效果是理解 4.0.0 种子特性最直观的素材。结语从 CHANGELOG 可以清晰地看到 Rough.js 4.x 的迭代主线3.1.0 补全填充样式家族 → 4.0.0 以扫描线算法与种子随机完成渲染内核重构 → 4.1.0 打通曲线填充 → 4.3.0 引入虚线、可控描边并修正 SVG 填充规则 → 4.4.0 解决路径拼接的顶点稳定性 → 4.5.0 收敛算法边界情形并加入输出精度控制。每一步改动都能在 src/ 目录的 renderer、generator、fillers 与 canvas/svg 双管线中找到一一对应的实现证据。掌握这些参数的源码级语义后无论是做手绘风格白板、图表可视化还是创意 Canvas 应用你都能精确控制 Rough.js 的每一处粗糙细节。赞分享图形学【免费下载链接】roughCreate graphics with a hand-drawn, sketchy, appearance项目地址https://gitcode.com/gh_mirrors/ro/rough点击查看免费下载相关推荐openreplay/tracker 版本演进全解析从 4.x 到 18.0.6 的会话录制 SDK 技术路线图openreplay/tracker 版本演进全解析从 4.x 到 18.0.6 的会话录制 SDK 技术路线图 本篇文章以 openreplay/tra可观测性开发工具前端后端AIHawk配置教程从零跑通invisible_playwright_mcp的隐身浏览器AgentAIHawk配置教程从零跑通invisible_playwright_mcp的隐身浏览器Agent invisible_playwright_mcp又名 A人工智能AI Agent浏览器控制GUI 自动化MCP 服务Ivysaur安全与优化保护你的文本数据隐私的终极指南Ivysaur安全与优化保护你的文本数据隐私的终极指南 在当今数据驱动的时代文本数据的隐私保护和模型优化已成为AI应用的核心挑战。Ivysaur作为一款高效上一篇3分钟掌握Neat Bookmarks终极树状书签管理解决方案下一篇Wand-Enhancer 完整指南从源码构建到 3 步跑通本地保留 WeMod Pro 还能手机遥控修改器创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考