2021-11-22 20:54:04 +01:00
|
|
|
class Canvas {
|
|
|
|
constructor(wrapperEl) {
|
|
|
|
this.allowDrawing = false;
|
2021-12-21 13:14:32 +01:00
|
|
|
this.viewbox = { "x": 0, "y": 0, "width": null, "height": null };
|
2021-11-22 20:54:04 +01:00
|
|
|
this.url = window.location.origin.replace('http', 'ws') + '/ws?' + window.location.search.substring(1);
|
2021-12-21 13:14:32 +01:00
|
|
|
|
|
|
|
// build the interface
|
2021-11-22 20:54:04 +01:00
|
|
|
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);
|
|
|
|
|
|
|
|
|
2021-12-20 13:36:18 +01:00
|
|
|
this.colors = ["black", "red", "blue", "green"];
|
2021-11-22 20:54:04 +01:00
|
|
|
|
|
|
|
this.resize();
|
|
|
|
|
|
|
|
window.addEventListener('resize', this.requestResize);
|
|
|
|
|
|
|
|
|
|
|
|
this.paths = [];
|
2021-12-21 13:14:32 +01:00
|
|
|
this.viewboxes = [];
|
|
|
|
this.events = []; // all paths & viewboxes events
|
2021-11-22 20:54:04 +01:00
|
|
|
this.isDrawing = false;
|
|
|
|
this.hasMouseDown = false;
|
|
|
|
this.currentStrokeEl = null;
|
|
|
|
|
|
|
|
this.startTime = null;
|
|
|
|
|
2021-12-21 13:14:32 +01:00
|
|
|
this.isMoving = false;
|
|
|
|
document.body.addEventListener('pointermove', (ev) => {
|
|
|
|
if (ev.pointerType == "touch" || ev.buttons & 4) { // 4: middle mouse button
|
|
|
|
this.moveCanvas(ev);
|
|
|
|
} else { // pointerType == pen or mouse
|
|
|
|
this.draw(ev);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
document.body.addEventListener('pointerup', (ev) => {
|
|
|
|
if (ev.pointerType == "touch" || ev.buttons & 4 || this.isMoving) { // buttons is 0 on pointerup
|
|
|
|
this.endMoveCanvas(ev);
|
|
|
|
this.isMoving = false;
|
|
|
|
} else { // pointerType == pen or mouse
|
|
|
|
this.penup(ev);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
this.svgEl.addEventListener('pointerdown', (ev) => {
|
|
|
|
if (ev.pointerType == "touch" || ev.buttons & 4) { // 4: middle mouse button
|
|
|
|
this.isMoving = true;
|
|
|
|
this.startMoveCanvas(ev);
|
|
|
|
} else { // pointerType == pen or mouse
|
|
|
|
this.startStroke(ev);
|
|
|
|
}
|
|
|
|
});
|
2021-11-22 20:54:04 +01:00
|
|
|
|
|
|
|
this.createToolbox();
|
|
|
|
|
|
|
|
this.setColor(this.colors[0]);
|
|
|
|
|
|
|
|
|
|
|
|
this.socket = new WebSocket(this.url);
|
|
|
|
this.socket.addEventListener('open', (e) => {
|
2021-12-21 13:14:32 +01:00
|
|
|
const d = {
|
|
|
|
'event': 'dimensions',
|
|
|
|
'width': this.viewbox.width,
|
|
|
|
'height': this.viewbox.height
|
|
|
|
};
|
|
|
|
console.log('send', d);
|
|
|
|
this.socket.send(JSON.stringify(d));
|
2021-11-22 20:54:04 +01:00
|
|
|
})
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-12-21 13:14:32 +01:00
|
|
|
startMoveCanvas(ev) {
|
|
|
|
this.moveCanvasPrevPoint = { "x": ev.x, "y": ev.y };
|
|
|
|
this.currentMoves = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
endMoveCanvas(ev) {
|
|
|
|
this.moveCanvasPrevPoint = null;
|
|
|
|
|
|
|
|
// sync viewpoints
|
|
|
|
const d = {
|
|
|
|
'event': 'viewbox',
|
|
|
|
'viewboxes': this.currentMoves
|
|
|
|
};
|
|
|
|
console.log('send', d);
|
|
|
|
this.socket.send(JSON.stringify(d));
|
|
|
|
}
|
|
|
|
|
|
|
|
moveCanvas(ev) {
|
|
|
|
if (this.moveCanvasPrevPoint === null) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
const diff = {
|
|
|
|
"x": ev.x - this.moveCanvasPrevPoint.x,
|
|
|
|
"y": ev.y - this.moveCanvasPrevPoint.y,
|
|
|
|
}
|
|
|
|
this.viewbox.x -= diff.x;
|
|
|
|
this.viewbox.y -= diff.y;
|
|
|
|
this.moveCanvasPrevPoint = { "x": ev.x, "y": ev.y };
|
|
|
|
this.currentMoves.push(Object.assign({'t': window.performance.now() - this.startTime}, this.viewbox));
|
|
|
|
|
|
|
|
this.applyViewBox()
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-22 20:54:04 +01:00
|
|
|
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() {
|
2021-12-21 13:14:32 +01:00
|
|
|
this.viewbox.width = window.innerWidth;
|
|
|
|
this.viewbox.height = window.innerHeight;
|
|
|
|
|
|
|
|
this.applyViewBox();
|
|
|
|
}
|
|
|
|
|
|
|
|
applyViewBox() {
|
|
|
|
const viewBox = `${this.viewbox.x} ${this.viewbox.y} ${this.viewbox.width} ${this.viewbox.height}`;
|
2021-11-22 20:54:04 +01:00
|
|
|
this.svgEl.setAttribute('viewBox', viewBox);
|
2021-12-21 13:14:32 +01:00
|
|
|
this.svgEl.setAttribute('width', this.viewbox.width + 'mm');
|
|
|
|
this.svgEl.setAttribute('height', this.viewbox.height + 'mm');
|
|
|
|
|
|
|
|
// todo save drag event;
|
|
|
|
// const newViewbox = Object.assign({}, this.viewbox, {'t': window.performance.now() - this.startTime});
|
|
|
|
// const lastViewbox = this.viewboxes[this.viewboxes.length - 1];
|
|
|
|
// if(newViewbox.x == lastViewbox.x && newViewbox.y == lastViewbox.y && newViewbox.width == lastViewbox.width && newViewbox.height == lastViewbox.height){
|
|
|
|
// // do nothing, avoiding duplicate
|
|
|
|
// } else {
|
|
|
|
// this.viewboxes.push(newViewbox);
|
|
|
|
// this.events.push(newViewbox);
|
|
|
|
// }
|
2021-11-22 20:54:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
2021-12-21 13:14:32 +01:00
|
|
|
let x = (e.x - box['left'] + this.viewbox.x) / box['width'];
|
|
|
|
let y = (e.y - box['top'] + this.viewbox.y) / box['height'];
|
2021-11-22 20:54:04 +01:00
|
|
|
return { 'x': x, 'y': y };
|
|
|
|
}
|
|
|
|
|
|
|
|
isInsideBounds(pos) {
|
|
|
|
return !(pos['x'] < 0 || pos['y'] < 0 || pos['x'] > 1 || pos['y'] > 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
draw(e) {
|
|
|
|
let pos = this.getCoordinates(e);
|
|
|
|
|
2021-12-21 13:14:32 +01:00
|
|
|
if (!this.isDrawing && this.hasMouseDown /*&& this.isInsideBounds(pos)*/) {
|
2021-11-22 20:54:04 +01:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
startStroke(e) {
|
|
|
|
this.hasMouseDown = true;
|
|
|
|
|
|
|
|
const strokeEl = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
|
|
strokeEl.style.stroke = this.currentColor;
|
|
|
|
this.svgEl.appendChild(strokeEl);
|
|
|
|
this.currentStrokeEl = strokeEl;
|
2021-12-21 13:14:32 +01:00
|
|
|
let path = {
|
2021-11-22 20:54:04 +01:00
|
|
|
'el': strokeEl,
|
|
|
|
'color': this.currentColor,
|
|
|
|
'points': []
|
2021-12-21 13:14:32 +01:00
|
|
|
};
|
|
|
|
this.paths.push(path);
|
|
|
|
this.events.push(path); // same ref.
|
2021-11-22 20:54:04 +01:00
|
|
|
|
|
|
|
if (this.startTime === null) {
|
|
|
|
// initiate timer on first stroke
|
|
|
|
this.startTime = window.performance.now();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
endStroke(pos) {
|
|
|
|
if (!this.isDrawing) {
|
|
|
|
return;
|
|
|
|
}
|
2021-12-21 13:14:32 +01:00
|
|
|
|
2021-11-22 20:54:04 +01:00
|
|
|
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];
|
2021-12-21 13:14:32 +01:00
|
|
|
const d = {
|
|
|
|
'event': 'stroke',
|
2021-11-22 20:54:04 +01:00
|
|
|
'color': stroke.color,
|
|
|
|
'points': stroke.points
|
2021-12-21 13:14:32 +01:00
|
|
|
};
|
|
|
|
console.log('send', d);
|
|
|
|
this.socket.send(JSON.stringify(d));
|
2021-11-22 20:54:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2021-12-21 13:14:32 +01:00
|
|
|
d += `M${stroke[0] * this.viewbox.width},${stroke[1] * this.viewbox.height} `;
|
2021-11-22 20:54:04 +01:00
|
|
|
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]];
|
2021-12-21 13:14:32 +01:00
|
|
|
d += `${rel_stroke[0] * this.viewbox.width},${rel_stroke[1] * this.viewbox.height} `;
|
2021-11-22 20:54:04 +01:00
|
|
|
}
|
|
|
|
last_stroke = stroke;
|
|
|
|
|
|
|
|
}
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|