Попытка использовать Rough.js для рендеринга частей сцены konva, но безуспешно. Rough использует ссылку на холст для рисования, но ни getCanvas(), ни toCanvas() на слое konva, похоже, не помогают. Идеи?
// 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);
Скрипка здесь: https://jsfiddle.net/qwLmb9ge/1/
Как правило, вы можете столкнуться с некоторыми помехами между библиотеками, например. неожиданные перерисовки, когда один очищает вывод содержимого другим.
Вы можете захотеть иметь 2 отдельных сложенных холста, один для Rough и один для Konva, чтобы они создавали вид единого холста, но сохраняли администрирование холста отдельно для каждой библиотеки. Это может оказаться невозможным, если вы хотите перекрыть объекты из двух библиотек по оси Z.
Также обратите внимание, что Konva использует холст для каждого слоя.
Возвращаясь к вашему вопросу, глядя на образец информации в Rough.js, кажется, что ему нужен простой элемент холста DOM, как в их примере:
const rc = rough.canvas(document.getElementById('canvas'));
чтобы получить экземпляр холста, на который вы хотите настроить таргетинг. Так как Konva использует стандартный элемент холста HTML, вы можете использовать любой механизм выбора, который работает в контексте вашей страницы, чтобы получить холст. В своей скрипке вы используете
<div id = "roughAndKonva"></div>
который создается в браузере (можно увидеть с помощью инструментов разработчика браузера)
<div id = "roughAndKonva">
<div class = "konvajs-content" role = "presentation" style = "...">
<canvas style = "...">
</canvas>
</div>
</div>
Поэтому вы можете получить доступ к этому элементу холста через подходящий селектор элементов. Если вы используете jquery, то следующее даст вам элемент холста, который использует Konva.
var konvaCanvas = $('#roughAndKonva>div>canvas')[0];
Вы также можете использовать простой JS для выбора холста.
Я изменил ваш код, как показано ниже (раздел комбинированного кода)
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);
Что производит вывод для комбинированных холстов, как показано ниже, что, я думаю, вы сделали после того, как заметили, что я намеренно сместил прямоугольник Konva, чтобы подчеркнуть, что оба присутствуют.
Действительно, вы можете - Konva и подобные библиотеки действуют как прокси, чтобы предоставить дополнительные возможности по сравнению со стандартным элементом холста HTML5, который всегда должен быть там. Библиотека обычно внедряет то, что она намеревается сделать какой-то частной оболочкой, как это делает Konva, но вы всегда можете взломать инспектор DOM в своем браузере, чтобы увидеть, как он устроен, а затем сформулировать селектор в соответствии с моим примером .
Я использовал ответ Vanquished Wombat выше, чтобы начать, но столкнулся с некоторыми проблемами с перерисовкой сцены, поскольку новый нарисованный прямоугольник затем будет стерт.
Решил эту проблему, нарисовав грубый прямоугольник на 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 предоставляет класс Форма для создания пользовательских фигур. У него есть метод 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);
}
});
Теперь вам в любом случае нужно создать экземпляр грубого холста, чтобы он знал, что мы рисуем на холсте:
const roughCanvas = rough.canvas(document.querySelector(`.${SHAPES_LAYER_CLS}`));
Этот экземпляр будет использоваться внутри sceneFunc(). Следующая проблема заключается в том, что вам нужно будет использовать предоставленный контекст (первый аргумент sceneFunc), а rough.js не предоставляет API для рисования с использованием контекста, но вы можете сделать что-то другое:
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);
}
});
Надеюсь, это имеет смысл. Это ссылка на мое использование - RoughRect.ts
Большое спасибо, я не знал, что мне нужно вернуться в DOM и получить его таким образом.