chronodiagram/www/draw.js

245 lines
7.4 KiB
JavaScript

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 = ["black", "red", "blue", "green"];
this.resize();
window.addEventListener('resize', this.requestResize);
this.paths = [];
this.isDrawing = false;
this.hasMouseDown = false;
this.currentStrokeEl = null;
this.startTime = null;
document.body.addEventListener('pointermove', this.draw.bind(this));
document.body.addEventListener('pointerup', this.penup.bind(this));
this.svgEl.addEventListener('pointerdown', this.startStroke.bind(this));
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;
}
}