Any discussion is welcomed. Thanks for reading!
What I am trying to do
I'm trying to implement simple paper(whiteboard) using Konva.js.
So far I've implemented Drag, Zoom and Free drawing on paper.
I referred to
- https://konvajs.org/docs/sandbox/Zooming_Relative_To_Pointer.html for Zoom
- https://konvajs.org/docs/sandbox/Free_Drawing.html for Free drawing
I want to draw only on the region with beige color background, and I want to draw exactly under the pointer even though it is zoomed or dragged.
But Free drawing and both Drag and Zoom features don't work well together.
Bug
Can't draw correctly after dragging or zooming.
Where I think wrong thing is happening, but can't fix
I think something is wrong in the 2 parts bellow.
- Implementation of zoom
- How I use stage.getPointerPosition() in drawing implementation
- Or implementations of these two doesn't fit together
Code
Minimum code is here.
/* ---- Mode management ---- */
let modeSelector = document.getElementById('mode-selector');
let mode = modeSelector.value;
modeSelector.addEventListener('change', () => {
// Discaed event handlers used by old mode
switch (mode) {
case 'Hand': {
endHand();
break;
}
case 'Pen': {
endPen();
break;
}
}
// Set event handlers for new mode
mode = modeSelector.value;
switch (mode) {
case 'Hand': {
useHand();
break;
}
case 'Pen': {
usePen();
break;
}
}
});
/* ---- Konva Objects ---- */
let stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
// A layer that is only used for background color
let backgroundLayer = new Konva.Layer();
let backgroundColor = new Konva.Image({
width: window.innerWidth,
height: window.innerHeight,
fill: 'rgb(242,233,226)'
})
backgroundLayer.add(backgroundColor);
stage.add(backgroundLayer);
backgroundLayer.draw();
// A layer for free drawing
let drawLayer = new Konva.Layer();
stage.add(drawLayer);
/* ---- Functions for mode change ----*/
function useHand () {
// Make stage draggable
stage.draggable(true);
// Make stage zoomable
// *** Code is copy and pasted from
// *** https://konvajs.org/docs/sandbox/Zooming_Relative_To_Pointer.htmlhttps://konvajs.org/docs/sandbox/Zooming_Relative_To_Pointer.html
let scaleBy = 1.3;
stage.on('wheel', (evt) => {
evt.evt.preventDefault();
let oldScale = stage.scaleX();
let mousePointTo = {
x: stage.getPointerPosition().x / oldScale - stage.x() / oldScale,
y: stage.getPointerPosition().y / oldScale - stage.y() / oldScale
};
let newScale = evt.evt.deltaY > 0 ? oldScale * scaleBy : oldScale / scaleBy;
stage.scale({ x: newScale, y: newScale });
let newPos = {
x: -(mousePointTo.x - stage.getPointerPosition().x / newScale) * newScale,
y: -(mousePointTo.y - stage.getPointerPosition().y / newScale) * newScale
};
stage.position(newPos);
stage.batchDraw();
});
}
function endHand () {
stage.draggable(false);
stage.off('wheel');
}
function usePen () {
let isDrawing = false;
let currentLine;
stage.on('mousedown', (evt) => {
// Start drawing
isDrawing = true;
// Create new line object
let pos = stage.getPointerPosition();
currentLine = new Konva.Line({
stroke: 'black',
strokeWidth: 3,
points: [pos.x, pos.y]
});
drawLayer.add(currentLine);
});
stage.on('mousemove', (evt) => {
if (!isDrawing) {
return;
}
// If drawing, add new point to the current line object
let pos = stage.getPointerPosition();
let newPoints = currentLine.points().concat([pos.x, pos.y]);
currentLine.points(newPoints);
drawLayer.batchDraw();
});
stage.on('mouseup', (evt) => {
// End drawing
isDrawing = false;
});
}
function endPen () {
stage.off('mousedown');
stage.off('mousemove');
stage.off('mouseup');
}
/* ---- Init ---- */
useHand();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Paper</title>
</head>
<body>
<select id="mode-selector">
<option value="Hand">Hand</option>
<option value="Pen">Pen</option>
</select>
<div id="container"></div>
<script src="https://unpkg.com/[email protected]/konva.min.js"></script>
<!-- <script src="konvaTest.js"></script> -->
<script src="buggyPaper.js"></script>
</body>
</html>