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; document.body.addEventListener('mousemove', this.draw.bind(this)); document.body.addEventListener('mouseup', this.penup.bind(this)); this.svgEl.addEventListener('mousedown', 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; } }