2021-11-22 19:54:04 +00:00
|
|
|
class Canvas {
|
|
|
|
constructor(wrapperEl) {
|
|
|
|
this.allowDrawing = false;
|
|
|
|
this.url = window.location.origin.replace('http', 'ws') + '/ws?' + window.location.search.substring(1);
|
|
|
|
this.wrapperEl = wrapperEl;
|
|
|
|
this.wrapperEl.classList.add('closed');
|
|
|
|
|
|
|
|
this.svgEl = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
|
|
this.wrapperEl.appendChild(this.svgEl);
|
|
|
|
|
|
|
|
this.toolboxEl = document.createElement('div');
|
|
|
|
this.toolboxEl.classList.add('toolbox')
|
|
|
|
this.wrapperEl.appendChild(this.toolboxEl);
|
|
|
|
|
|
|
|
this.filenameEl = document.createElement('div');
|
|
|
|
this.filenameEl.classList.add('filename')
|
|
|
|
this.wrapperEl.appendChild(this.filenameEl);
|
|
|
|
|
|
|
|
|
|
|
|
this.colors = ["red", "blue", "green"];
|
|
|
|
|
|
|
|
this.resize();
|
|
|
|
|
|
|
|
window.addEventListener('resize', this.requestResize);
|
|
|
|
|
|
|
|
|
|
|
|
this.paths = [];
|
|
|
|
this.isDrawing = false;
|
|
|
|
this.hasMouseDown = false;
|
|
|
|
this.currentStrokeEl = null;
|
|
|
|
|
|
|
|
this.startTime = null;
|
|
|
|
|
2021-11-23 11:28:40 +00:00
|
|
|
document.body.addEventListener('pointermove', this.draw.bind(this));
|
|
|
|
document.body.addEventListener('pointerup', this.penup.bind(this));
|
|
|
|
this.svgEl.addEventListener('pointerdown', this.startStroke.bind(this));
|
2021-11-22 19:54:04 +00:00
|
|
|
|
|
|
|
this.createToolbox();
|
|
|
|
|
|
|
|
this.setColor(this.colors[0]);
|
|
|
|
|
|
|
|
|
|
|
|
this.socket = new WebSocket(this.url);
|
|
|
|
this.socket.addEventListener('open', (e) => {
|
|
|
|
|
|
|
|
this.socket.send(JSON.stringify({
|
|
|
|
'action': 'dimensions',
|
|
|
|
'width': this.width,
|
|
|
|
'height': this.height
|
|
|
|
}));
|
|
|
|
})
|
|
|
|
this.socket.addEventListener('message', (e) => {
|
|
|
|
let msg = JSON.parse(e.data);
|
|
|
|
console.log('receive', msg);
|
|
|
|
if (msg.hasOwnProperty('filename')) {
|
|
|
|
console.log('filename', msg.filename);
|
|
|
|
this.setFilename(msg.filename);
|
|
|
|
this.openTheFloor()
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
openTheFloor() {
|
|
|
|
this.wrapperEl.classList.remove('closed');
|
|
|
|
}
|
|
|
|
|
|
|
|
setFilename(filename) {
|
|
|
|
this.filename = filename;
|
|
|
|
this.filenameEl.innerText = filename;
|
|
|
|
}
|
|
|
|
|
|
|
|
createToolbox() {
|
|
|
|
const colorsEl = document.createElement('ul');
|
|
|
|
colorsEl.classList.add('colors');
|
|
|
|
for (let color of this.colors) {
|
|
|
|
const colorEl = document.createElement('li');
|
|
|
|
colorEl.style.background = color;
|
|
|
|
colorEl.addEventListener('click', (e) => {
|
|
|
|
console.log('set color', color)
|
|
|
|
this.setColor(color);
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
colorsEl.appendChild(colorEl);
|
|
|
|
}
|
|
|
|
this.toolboxEl.appendChild(colorsEl);
|
|
|
|
}
|
|
|
|
|
|
|
|
setColor(color) {
|
|
|
|
this.currentColor = color;
|
|
|
|
const colorEls = this.toolboxEl.querySelectorAll('.colors li');
|
|
|
|
for (let colorEl of colorEls) {
|
|
|
|
if (colorEl.style.backgroundColor == color) {
|
|
|
|
colorEl.classList.add('selected');
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
colorEl.classList.remove('selected');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
resize() {
|
|
|
|
this.width = window.innerWidth;
|
|
|
|
this.height = window.innerHeight;
|
|
|
|
const viewBox = `0 0 ${this.width} ${this.height}`;
|
|
|
|
this.svgEl.setAttribute('viewBox', viewBox);
|
|
|
|
this.svgEl.setAttribute('width', this.width + 'mm');
|
|
|
|
this.svgEl.setAttribute('height', this.height + 'mm');
|
|
|
|
}
|
|
|
|
|
|
|
|
requestResize() {
|
|
|
|
alert('Resize not implemented yet. Please reloade the page');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
getCoordinates(e) {
|
|
|
|
// convert event coordinates into relative positions on x & y axis
|
|
|
|
let box = this.svgEl.getBoundingClientRect();
|
|
|
|
let x = (e.x - box['left']) / box['width'];
|
|
|
|
let y = (e.y - box['top']) / box['height'];
|
|
|
|
return { 'x': x, 'y': y };
|
|
|
|
}
|
|
|
|
|
|
|
|
isInsideBounds(pos) {
|
|
|
|
return !(pos['x'] < 0 || pos['y'] < 0 || pos['x'] > 1 || pos['y'] > 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// isInsideDrawingBounds (pos) {
|
|
|
|
// if(pos['x'] > xPadding && pos['x'] < (xPadding+drawWidthFactor) && pos['y'] > yPadding && pos['y'] < yPadding+drawHeightFactor) {
|
|
|
|
// return true;
|
|
|
|
// }
|
|
|
|
// return false;
|
|
|
|
// }
|
|
|
|
|
|
|
|
draw(e) {
|
|
|
|
let pos = this.getCoordinates(e);
|
|
|
|
if (this.hasMouseDown)
|
|
|
|
console.log(pos, e);
|
|
|
|
|
|
|
|
// if(!isInsideBounds(pos)) {
|
|
|
|
// // outside of bounds
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// if(!e.buttons || (this.isDrawing && !this.isInsideBounds(pos))){
|
|
|
|
// this.endStroke(pos);
|
|
|
|
// }
|
|
|
|
if (!this.isDrawing && this.hasMouseDown && this.isInsideBounds(pos)) {
|
|
|
|
this.isDrawing = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.isDrawing) {
|
|
|
|
this.paths[this.paths.length - 1].points.push([pos['x'], pos['y'], 0, window.performance.now() - this.startTime]);
|
|
|
|
let d = this.strokes2D(this.paths[this.paths.length - 1].points);
|
|
|
|
this.currentStrokeEl.setAttribute('d', d);
|
|
|
|
}
|
|
|
|
|
|
|
|
//console.log([pos['x'], pos['y']], isDrawing);
|
|
|
|
// socket.send(JSON.stringify({
|
|
|
|
// 'action': 'move',
|
|
|
|
// 'direction': [pos['x'], pos['y']],
|
|
|
|
// 'mouse': isDrawing,
|
|
|
|
// }));
|
|
|
|
}
|
|
|
|
|
|
|
|
startStroke(e) {
|
|
|
|
this.hasMouseDown = true;
|
|
|
|
console.log('start');
|
|
|
|
|
|
|
|
const strokeEl = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
|
|
strokeEl.style.stroke = this.currentColor;
|
|
|
|
this.svgEl.appendChild(strokeEl);
|
|
|
|
this.currentStrokeEl = strokeEl;
|
|
|
|
this.paths.push({
|
|
|
|
'el': strokeEl,
|
|
|
|
'color': this.currentColor,
|
|
|
|
'points': []
|
|
|
|
});
|
|
|
|
|
|
|
|
if (this.startTime === null) {
|
|
|
|
// initiate timer on first stroke
|
|
|
|
this.startTime = window.performance.now();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
endStroke(pos) {
|
|
|
|
console.log(this.isDrawing)
|
|
|
|
if (!this.isDrawing) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
console.log("send!")
|
|
|
|
this.isDrawing = false;
|
|
|
|
//document.body.removeEventListener('mousemove', draw);
|
|
|
|
|
|
|
|
|
|
|
|
if (this.paths[this.paths.length - 1].points.length > 0) {
|
|
|
|
// mark point as last of stroke
|
|
|
|
this.paths[this.paths.length - 1].points[this.paths[this.paths.length - 1].points.length - 1][2] = 1;
|
|
|
|
}
|
|
|
|
const stroke = this.paths[this.paths.length - 1];
|
|
|
|
this.socket.send(JSON.stringify({
|
|
|
|
'action': 'stroke',
|
|
|
|
'color': stroke.color,
|
|
|
|
'points': stroke.points
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
penup(e) {
|
|
|
|
if (!this.hasMouseDown) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.hasMouseDown = false;
|
|
|
|
|
|
|
|
let pos = this.getCoordinates(e);
|
|
|
|
this.endStroke(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
strokes2D(strokes) {
|
|
|
|
// strokes to a d attribute for a path
|
|
|
|
let d = "";
|
|
|
|
let last_stroke = undefined;
|
|
|
|
let cmd = "";
|
|
|
|
for (let stroke of strokes) {
|
|
|
|
if (!last_stroke) {
|
|
|
|
d += `M${stroke[0] * this.width},${stroke[1] * this.height} `;
|
|
|
|
cmd = 'M';
|
|
|
|
} else {
|
|
|
|
if (last_stroke[2] == 1) {
|
|
|
|
d += " m";
|
|
|
|
cmd = 'm';
|
|
|
|
} else if (cmd != 'l') {
|
|
|
|
d += ' l ';
|
|
|
|
cmd = 'l';
|
|
|
|
}
|
|
|
|
let rel_stroke = [stroke[0] - last_stroke[0], stroke[1] - last_stroke[1]];
|
|
|
|
d += `${rel_stroke[0] * this.width},${rel_stroke[1] * this.height} `;
|
|
|
|
}
|
|
|
|
last_stroke = stroke;
|
|
|
|
|
|
|
|
}
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|