使用 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 来使用上下文进行绘制,但是您可以做一些不同的事情:
- 使用generator API创建可绘制对象
- 使用 code from the rough.js library,它可以根据可绘制对象的指令创建形状并在提供的上下文中绘制(我将这段代码保存在 roughService 中)
- 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
尝试使用 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 来使用上下文进行绘制,但是您可以做一些不同的事情:
- 使用generator API创建可绘制对象
- 使用 code from the rough.js library,它可以根据可绘制对象的指令创建形状并在提供的上下文中绘制(我将这段代码保存在 roughService 中)
- 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