大家都在找的手绘/素描风格图编辑器它它它来了

我正在做的开源项目:du-editor,一个基于X6的图编辑器

要在 X6 中实现手绘/素描风格,核心是 SVG 滤镜 (Filter)SVG 填充模式 (Pattern) 。X6 的节点和边都基于 SVG,因此我们可以充分利用 SVG 的强大功能来创建这种独特的视觉效果。

这种风格主要包含三个要素:

  1. 抖动、不规则的边框 :这通过 SVG 的 feTurbulencefeDisplacementMap 滤镜实现。
  2. 阴影线填充效果 :这通过 SVG 的 <pattern> 元素创建可平铺的填充图案来实现。
  3. 手写体字体:选择一个合适的手写风格字体

第 1 步:定义 SVG 滤镜和填充模式

首先,需要在你的 HTML 文件中,通常是在图表容器 <div> 的旁边,定义好我们需要的 SVG 资源。我们将它们放在一个 <svg> 标签的 <defs> 元素中,这样 X6 就可以通过 ID 引用它们。

HTML

ini 复制代码
<div id="container"></div>

<svg width="0" height="0">
  <defs>
    <filter id="filter-sketch">
      <feTurbulence
        type="fractalNoise"
        baseFrequency="0.01"
        numOctaves="5"
        result="noise"
      />
      <feDisplacementMap
        in="SourceGraphic"
        in2="noise"
        scale="5"
        xChannelSelector="R"
        yChannelSelector="G"
        result="displaced"
      />
    </filter>

    <pattern
      id="pattern-hatch"
      patternUnits="userSpaceOnUse"
      width="8"
      height="8"
      patternTransform="rotate(45)"
    >
      <path
        d="M -1,1 l 2,-2 M 0,8 l 8,-8 M 7,9 l 2,-2"
        stroke="#c58d6a"
        stroke-width="1"
      />
    </pattern>
  </defs>
</svg>
  • <filter id="filter-sketch"> :

    • feTurbulence: 用于生成一种叫做 "Perlin noise" 的伪随机噪点图。baseFrequency 控制噪点的"波纹"大小,值越小,线条抖动越平缓。numOctaves 决定了细节层次。
    • feDisplacementMap: 将上面生成的噪点图应用到原始图形上,使其像素发生位移,从而产生抖动的"手绘"效果。scale 属性控制抖动的剧烈程度,是调整效果最关键的参数。
  • <pattern id="pattern-hatch"> :

    • 我们创建了一个 8x8 像素大小的单元格,并将其旋转了 45 度。
    • 在单元格内部,我们用 <path> 画了几条斜线。
    • 这个 pattern 会像瓷砖一样自动平铺,以填充整个形状。你可以通过修改 width, height<path>stroke-width 来调整阴影线的密度和粗细。

第 2 步:在 X6 中注册自定义节点和样式

接下来,我们在 X6代码中注册新的节点,并在其 attrs 中引用上面定义的滤镜和填充。

JavaScript 复制代码
// 准备好图表实例
const graph = new Graph({
  container: document.getElementById('container'),
  // ... 其他配置
});

// --- 注册手绘风格的节点 ---

// 1. 手绘风格 - 圆角矩形 (对应图片右侧的形状)
Graph.registerNode('sketch-rect', {
  inherit: 'rect', // 继承自矩形
  attrs: {
    body: {
      // 关键样式
      stroke: '#a26740',
      strokeWidth: 2,
      fill: 'url(#pattern-hatch)', // 使用阴影线填充
      filter: 'url(#filter-sketch)', // 应用手绘滤镜
    },
    label: {
      // 推荐使用手写体
      fontFamily: '"Comic Sans MS", "Caveat", cursive',
      fill: '#a26740',
      fontSize: 14,
    },
  },
});

// 2. 手绘风格 - 菱形 (对应图片左侧的形状)
// 菱形需要自定义 SVG 路径
const diamondPath = 'M 30 0 L 60 30 L 30 60 L 0 30 Z';
Graph.registerNode('sketch-diamond', {
  inherit: 'path', // 继承自路径
  width: 60,
  height: 60,
  attrs: {
    body: {
      // 关键样式
      refD: diamondPath,
      stroke: '#a26740',
      strokeWidth: 2,
      fill: 'url(#pattern-hatch)',
      filter: 'url(#filter-sketch)',
    },
    label: {
      fontFamily: '"Comic Sans MS", "Caveat", cursive',
      fill: '#a26740',
      fontSize: 14,
    },
  },
});

第 3 步:配置边的样式

边的样式也需要应用相同的滤镜,可以在图表初始化配置中统一设置。

JavaScript 复制代码
const graph = new Graph({
  container: document.getElementById('container'),
  grid: true,
  // ...
  // --- 配置边的连接样式 ---
  connecting: {
    router: 'manhattan',
    createEdge() {
      return new Shape.Edge({
        attrs: {
          line: {
            stroke: '#a26740',
            strokeWidth: 2,
            filter: 'url(#filter-sketch)', // 为边线应用滤镜
            targetMarker: {
              name: 'block', // 箭头样式
              width: 12,
              height: 8,
              // 注意:需要给箭头也加上滤镜和填充/描边
              attrs: {
                  filter: 'url(#filter-sketch)',
              }
            },
          },
        },
      });
    },
  },
});

效果调整

可以实现一个完整的手绘风格主题。如果你想微调效果,可以重点关注:

  • 抖动程度 :修改 <filter> 中的 scale 值。值越大,抖动越厉害。
  • 阴影密度 :修改 <pattern>width, height 和内部 <path>stroke-width
  • 颜色 :直接在 X6 节点的 attrs 中修改 strokefill 颜色,以及在 <pattern> 中修改 stroke 颜色。
  • 字体 :为了更好的效果,你可以通过 CSS 引入一些免费的在线手写字体,例如 Google Fonts 上的 CaveatPatrick Hand
相关推荐
小白而已3 小时前
协程&挂起&恢复
前端
Mintopia3 小时前
单体 vs 微服务:当 Next.js 长成“巨石阵”以后 🪨➡️🧩
前端·后端·全栈
陈随易3 小时前
改变世界的编程语言MoonBit:配置系统介绍(上)
前端·后端·程序员
Zhencode3 小时前
CSS变量的应用
前端·css
Mintopia3 小时前
AIGC 训练数据的隐私保护技术:联邦学习在 Web 场景的落地
前端·javascript·aigc
鹏多多3 小时前
React项目集成苹果登录react-apple-signin-auth插件手把手指南
前端·javascript·react.js
白水先森3 小时前
Python 字符串与布尔值详解
java·服务器·前端
TZOF3 小时前
TypeScript的新类型(五):tuple元组
前端·后端·typescript
TZOF3 小时前
TypeScript的object大小写的区别
前端·后端·typescript