使用 Rough.js 作为 Konva 渲染器

Use Rough.js as a Konva renderer

尝试使用 Rough.js 渲染 konva 舞台的一部分但没有成功。 Rough 使用对 canvas 的引用来绘制,但 konva 层上的 getCanvas() 和 toCanvas() 似乎都无法解决问题。想法?

// Combined code
const cstage = new Konva.Stage({
   container: 'roughAndKonva',
   width: 200,
   height: 200
});
const clayer = new Konva.Layer();
var konvaCanvas = clayer.getCanvas()
const crc = rough.canvas(konvaCanvas);
crc.rectangle(50, 50, 100, 100);
cstage.add(clayer);

Fiddle 这里:https://jsfiddle.net/qwLmb9ge/1/

一般来说,您可能会在库之间遇到一些干扰,例如一个意外重绘清除另一个输出的内容。

您可能需要考虑使用 2 个单独的、堆叠的 canvases,一个用于 Rough,一个用于 Konva,这样它们看起来就像一个 canvas,但保留 canvas 管理员为每个库分开。如果您想重叠 z-axis 中两个库中的对象,这可能是不可能的。

另请注意,Konva 每层使用 canvas。

回到你的问题,查看 Rough.js 的示例信息,它似乎需要一个普通的 canvas DOM 元素,如他们的示例所示:

const rc = rough.canvas(document.getElementById('canvas'));

获取您希望定位的 canvas 实例。由于 Konva 使用标准 HTML canvas 元素,您可以使用任何 select 或在页面上下文中工作的机制来获取 canvas。在你的 fiddle 你使用

<div id="roughAndKonva"></div>

在浏览器中生成(可以通过浏览器开发工具查看)

<div id="roughAndKonva">
  <div class="konvajs-content" role="presentation" style="...">
    <canvas style="...">
    </canvas>
  </div>
</div>

因此您可以通过合适的元素 select 或访问此 canvas 元素。如果您使用的是 jquery,那么以下内容将为您提供 Konva 使用的 canvas 元素。

var konvaCanvas = $('#roughAndKonva>div>canvas')[0];

您也可以使用纯 JS 来 select canvas。

我修改了你的代码如下(组合代码部分)

const stage = new Konva.Stage({
   container: 'container',
   width: 200,
   height: 200
});

const layer = new Konva.Layer();
stage.add(layer);

const rect = new Konva.Rect({
   x : 50, y : 50, width: 100, height: 100,
   fill: 'black',
   draggable: true
});

layer.add(rect).draw();


// Rough.js sample code
const rc = rough.canvas(document.getElementById('canvas'));
rc.rectangle(50, 50, 100, 100);


// Combined code  MODS HERE !
const cstage = new Konva.Stage({
   container: 'roughAndKonva',
   width: 200,
   height: 200
});
const clayer = new Konva.Layer();
const rect2 = new Konva.Rect({
   x : 40, y : 40, width: 100, height: 100,
   fill: 'red',
   draggable: true
});
clayer.add(rect2).draw();
cstage.add(clayer).draw()

var konvaCanvas = $('#roughAndKonva>div>canvas')[0]
const crc = rough.canvas(konvaCanvas);
crc.rectangle(50, 50, 100, 100);

它为组合 canvases 生成如下输出,这就是我猜你在注意到我故意偏移 Konva 矩形以强调两者都存在之后的结果。

我使用上面 Vanquished Wombat 的答案开始,但是 运行 遇到了一些问题 re-drawing 场景,因为绘制的新矩形将被擦除。

通过将 rough-Rectangle 绘制到 SVG 上然后将 SVG 转换为可以添加到 Konva 上的图像来解决此问题。这使得它非常适合 Konva JS 例程,包括重绘。

// Background box
  const svg = $('<svg xmlns="http://www.w3.org/2000/svg"></svg>')[0];
  svg.setAttribute("height", startWidth);
  svg.setAttribute("width", startHeight);
  const rc = rough.svg(svg);
  svg.appendChild(rc.rectangle(0, 0, startWidth, startHeight, {roughness: 0.3, strokeWidth: 2}));
  svgToImg(svg).then(background => {
    const bgImage = new Konva.Image({
        x: 0,
        y: 0,
        image: background,
        name: 'depLine',
        width: background.width,
        height: background.height
      });
    board.add(bgImage);
    board.draw();
  });

svgToImg 函数:

// Promise to convert SVG to Image
function svgToImg(svg) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    const svgBlob = new Blob([svg.outerHTML], {type:'image/svg+xml'});
    const url = DOMURL.createObjectURL(svgBlob);
    img.onload = function() {
        DOMURL.revokeObjectURL(url);
        resolve(this);
    };
    img.onerror = reject
    img.src = url;
  });
}

我遇到了类似的问题,因此决定尝试让这两个库相互协作。

首先,Konva 提供了 Shape class 来创建自定义形状。它具有可用于绘制粗略形状的 sceneFunc() 方法。这是来自官方文档的示例:

const customShape = new Konva.Shape({
  x: 5,
  y: 10,
  fill: 'red',
  // a Konva.Canvas renderer is passed into the sceneFunc function
  sceneFunc (context, shape) {
    context.beginPath();
    context.moveTo(200, 50);
    context.lineTo(420, 80);
    context.quadraticCurveTo(300, 100, 260, 170);
    context.closePath();
    // Konva specific method
    context.fillStrokeShape(shape);
  }
});

现在你无论如何都需要创建一个 rough canvas 的实例,所以它会知道我们正在绘制 canvas:

const roughCanvas = rough.canvas(document.querySelector(`.${SHAPES_LAYER_CLS}`));

该实例将在 sceneFunc() 内部使用。 下一个问题是您需要使用提供的上下文(sceneFunc 的第一个参数)并且 rough.js 不提供 API 来使用上下文进行绘制,但是您可以做一些不同的事情:

  1. 使用generator API创建可绘制对象
  2. 使用 code from the rough.js library,它可以根据可绘制对象的指令创建形状并在提供的上下文中绘制(我将这段代码保存在 roughService 中)
  3. konva 会经常调用 sceneFunc(),因此您可以缓存可绘制对象并使用它,以免在每次渲染时都生成新形状。
const roughShape = new Konva.Shape({
    x: this.props.x || 0,
    y: this.props.y || 0,
    width: this.props.width || 0,
    height: this.props.height || 0,
    stroke: '#000000',
    strokeWidth: this.props.strokeWidth,
    fill: 'transparent',
    draggable: true,
    sceneFunc: (context, shape) => {
        if (isSelected()) {
            lastDrawable = roughCanvas.generator.rectangle(
                0,
                0,
                shape.getWidth(),
                shape.getHeight(),
                {
                    roughness: ROUGHNESS,
                    stroke: '#000000',
                },
            );
        }
        roughService.draw(context, lastDrawable);
        context.fillStrokeShape(shape);
    }
});

希望这是有道理的。这是 link 我的用法 - RoughRect.ts