let Konva;
try {
// @ts-ignore
Konva = require('konva');
} catch (e) {
Konva = {
Stage: class {
constructor(_options) {
throw new Error("Konva not loaded");
}
}
};
}
const Canvas = require('./canvas');
const Outline = require('./outline');
const Piece = require('./piece');
const Pair = require('./pair');
const {vector, ...Vector} = require('./vector');
const Painter = require('./painter');
/**
* @private
* @param {Piece} model
* @param {*} group
*/
function currentPositionDiff(model, group) {
return Pair.diff(group.x(),group.y(), model.metadata.currentPosition.x, model.metadata.currentPosition.y);
}
/**
* A {@link Painter} that uses Konva.js as rendering backend
* @implements {Painter}
*/
class KonvaPainter extends Painter {
/** @typedef {import('./canvas').Figure} Figure */
/** @typedef {import('./canvas').Group} Group */
/**
* @param {Canvas} canvas
* @param {string} id
*/
initialize(canvas, id) {
var stage = new Konva.Stage({
container: id,
width: canvas.width,
height: canvas.height,
draggable: !canvas.fixed,
});
this._initializeLayer(stage, canvas);
}
_initializeLayer(stage, canvas) {
var layer = new Konva.Layer();
stage.add(layer);
canvas['__konvaLayer__'] = layer;
}
/**
* @param {Canvas} canvas
*/
draw(canvas) {
canvas['__konvaLayer__'].draw();
}
/**
* @param {Canvas} canvas
*/
reinitialize(canvas) {
const layer = canvas['__konvaLayer__'];
const stage = layer.getStage();
layer.destroy();
this._initializeLayer(stage, canvas);
}
/**
* @param {Canvas} canvas
* @param {number} width
* @param {number} height
*/
resize(canvas, width, height) {
const layer = canvas['__konvaLayer__'];
const stage = layer.getStage();
stage.width(width);
stage.height(height);
}
/**
* @param {Canvas} canvas
* @param {import('./vector').Vector} factor
*/
scale(canvas, factor) {
canvas['__konvaLayer__'].getStage().scale(factor);
}
/**
*
* @param {Canvas} canvas
* @param {Piece} piece
* @param {Figure} figure
* @param {import('./outline').Outline} outline
*/
sketch(canvas, piece, figure, outline) {
figure.group = new Konva.Group({
x: piece.metadata.currentPosition.x,
y: piece.metadata.currentPosition.y,
draggable: !piece.metadata.fixed,
dragBoundFunc: canvas.preventOffstageDrag ? (position) => {
const furthermost = Vector.minus(vector(canvas.width, canvas.height), piece.size.radius);
return Vector.max(Vector.min(position, furthermost), piece.size.radius);
} : null,
});
figure.shape = new Konva.Line({
points: outline.draw(piece, piece.diameter, canvas.borderFill),
bezier: outline.isBezier(),
tension: outline.isBezier() ? null : canvas.lineSoftness,
stroke: piece.metadata.strokeColor || canvas.strokeColor,
strokeWidth: canvas.strokeWidth,
closed: true,
...Vector.multiply(piece.radius, -1),
});
this.fill(canvas, piece, figure);
figure.group.add(figure.shape);
canvas['__konvaLayer__'].add(figure.group);
}
/**
* @param {Canvas} canvas
* @param {Piece} piece
* @param {Figure} figure
*/
fill(canvas, piece, figure) {
const image = canvas.imageMetadataFor(piece);
figure.shape.fill(!image ? piece.metadata.color || 'black' : null);
figure.shape.fillPatternImage(image && image.content);
figure.shape.fillPatternScale(image && {x: image.scale, y: image.scale});
figure.shape.fillPatternOffset(image && Vector.divide(image.offset, image.scale));
}
/**
*
* @param {Canvas} _canvas
* @param {Piece} piece
* @param {Figure} figure
*/
label(_canvas, piece, figure) {
figure.label = new Konva.Text({
...Vector.minus({
x: piece.metadata.label.x || (figure.group.width() / 2),
y: piece.metadata.label.y || (figure.group.height() / 2),
}, piece.radius),
text: piece.metadata.label.text,
fontSize: piece.metadata.label.fontSize,
fontFamily: piece.metadata.label.fontFamily || 'Sans Serif',
fill: piece.metadata.label.color || 'white',
});
figure.group.add(figure.label);
}
/**
*
* @param {Canvas} _canvas
* @param {Group} group
* @param {Piece} piece
*/
physicalTranslate(_canvas, group, piece) {
group.x(piece.centralAnchor.x);
group.y(piece.centralAnchor.y);
}
/**
* @param {Canvas} _canvas
* @param {Piece} piece
* @param {*} group
*/
logicalTranslate(_canvas, piece, group) {
Vector.update(piece.metadata.currentPosition, group.x(), group.y());
}
/**
* @param {Canvas} canvas
* @param {Piece} piece
* @param {Group} group
* @param {import('./painter').VectorAction} f
*/
onDrag(canvas, piece, group, f) {
group.on('mouseover', () => {
document.body.style.cursor = 'pointer';
});
group.on('mouseout', () => {
document.body.style.cursor = 'default';
});
group.on('dragmove', () => {
let [dx, dy] = currentPositionDiff(piece, group);
group.zIndex(canvas.figuresCount - 1);
f(dx, dy);
});
}
/**
* @param {Canvas} _canvas
* @param {Piece} _piece
* @param {Group} group
* @param {import('./painter').Action} f
*/
onDragEnd(_canvas, _piece, group, f) {
group.on('dragend', () => {
f()
});
}
/**
* @param {Canvas} canvas
* @param {object} gestures
*/
registerKeyboardGestures(canvas, gestures) {
const container = canvas['__konvaLayer__'].getStage().container();
container.tabIndex = -1;
this._registerKeyDown(canvas, container, gestures);
this._registerKeyUp(canvas, container, gestures);
}
_registerKeyDown(canvas, container, gestures) {
container.addEventListener('keydown', function(e) {
for (let keyCode in gestures) {
if (e.keyCode == keyCode) {
gestures[keyCode](canvas.puzzle)
}
}
});
}
_registerKeyUp(canvas, container, gestures) {
container.addEventListener('keyup', function(e) {
for (let keyCode in gestures) {
if (e.keyCode == keyCode) {
canvas.puzzle.tryDisconnectionWhileDragging();
}
}
});
}
}
module.exports = KonvaPainter;