var serverAddress = document.getElementById("server-address").value; var serverPort = document.getElementById("server-port").value var socket//new WebSocket(`wss://${serverAddress}:${serverPort}`); var canvas = document.getElementById("board"); var colorInput = document.getElementById("color"); var canvasHidden = document.getElementById("hidden"); var timer = document.getElementById("timer"); var ctx = canvas.getContext("2d"); var ctx2 = canvasHidden.getContext("2d"); var imgData = ctx2.createImageData(canvasHidden.width, canvasHidden.height) var currentColor = "#000000" var timeBetweenPixels = undefined; canvas.width = 1920; canvas.height = 1080; canvasHidden.width = 1920; canvasHidden.height = 1080; function drawTimer() { timer.textContent = Number(timer.textContent) > 0? Number(timer.textContent - 1): 0 } function connect() { // socket.close(); serverAddress = document.getElementById("server-address").value; serverPort = document.getElementById("server-port").value console.log(`Connecting ${serverAddress}:${serverPort}`) socket = new WebSocket(`wss://${serverAddress}:${serverPort}`) // socket = new WebSocket(`wss://127.0.0.1:8080`) timer.textContent="Board is loading, please wait"; socket.addEventListener("open", (event) => { socket.send("{\"code\":0}"); }); socket.addEventListener("message", (event) => { // console.log("Message from server ", JSON.stringify(event.data.toString())); event.data.text().then(function(packet){ packet = JSON.parse(packet) let code = packet.code let content = packet.content switch (code){ case 0: timeBetweenPixels = packet.timeBetweenPixels; timer.textContent = 0; setInterval(() => { drawTimer(); }, 1000); console.log(`time between pixels ${timeBetweenPixels}`) //Ineffective way to do that, fix that later ;) let converted = Uint8ClampedArray.from(content) for (let i = 0; i < converted.length; i ++) imgData.data[i] = converted[i] redraw(); break; case 1: contentJson = JSON.parse(content); let color = { r: contentJson.r, g: contentJson.g, b: contentJson.b } if (packet.pixelOwner == true) timer.textContent = timeBetweenPixels; drawPixel(contentJson.x * 4, contentJson.y * 4, color) redraw(); break; } }); }); // socket.send("{\"code\":0}"); } function drawPixel(x, y, color) { let pixelNumber; if (y > 0) pixelNumber = (y) * canvas.width + x if (y == 0) pixelNumber = x if (color == undefined) rgbColor = hexToRgb(currentColor); for (let l = 0; l < 4; l ++) for (let l = 0; l < 5; l ++){ imgData.data[pixelNumber + 0] = color == undefined? rgbColor.r : color.r; imgData.data[pixelNumber + 1] = color == undefined? rgbColor.g : color.g; imgData.data[pixelNumber + 2] = color == undefined? rgbColor.b : color.b; imgData.data[pixelNumber + 3] = 255; } redraw(); } function redraw() { ctx.save(); ctx.setTransform(1,0,0,1,0,0); ctx.clearRect(0,0,canvas.width,canvas.height); ctx.restore(); ctx2.putImageData(imgData, 0, 0); ctx.drawImage(canvasHidden, 0, 0) ctx.imageSmoothingEnabled = false } // All the code that is making zooms and moving around cavnas is made with big help of http://phrogz.net/tmp/canvas_zoom_to_cursor.html. Thanks alot! window.onload = function() { connect(); var ctx = canvas.getContext("2d"); trackTransforms(ctx); var lastX=canvas.width/2, lastY=canvas.height/2; var dragStart,dragged; canvas.addEventListener('mousedown',function(evt){ if (evt.button == 1 || 1 == evt.button&2){ document.body.style.mozUserSelect = document.body.style.webkitUserSelect = document.body.style.userSelect = 'none'; lastX = evt.offsetX || (evt.pageX - canvas.offsetLeft); lastY = evt.offsetY || (evt.pageY - canvas.offsetTop); dragStart = ctx.transformedPoint(lastX,lastY); dragged = false; } },false); canvas.addEventListener('mousemove',function(evt){ lastX = evt.offsetX || (evt.pageX - canvas.offsetLeft); lastY = evt.offsetY || (evt.pageY - canvas.offsetTop); dragged = true; if (dragStart){ var pt = ctx.transformedPoint(lastX,lastY); ctx.translate(pt.x-dragStart.x,pt.y-dragStart.y); redraw(); } },false); canvas.addEventListener('mouseup',function(evt){ if (evt.button == 1 || 1 == evt.button&2){ dragStart = null; if (dragged) { let point = ctx.transformedPoint( evt.clientX - canvas.offsetLeft, evt.clientY - canvas.offsetTop ); } else { let point = { x: Math.floor(ctx.transformedPoint(lastX,lastY).x) * 4, y: Math.floor(ctx.transformedPoint(lastX,lastY).y) * 4, }; x = point.x; y = point.y; let pixelNumber; if (y > 0) pixelNumber = (y) * canvas.width + x if (y == 0) pixelNumber = x let color = { r: imgData.data[pixelNumber + 0], g: imgData.data[pixelNumber + 1], b: imgData.data[pixelNumber + 2] }; changeColor(rgbToHex(color.r, color.g, color.b)); } } else if (evt.button == 0) { let point = { x: Math.floor(ctx.transformedPoint(lastX,lastY).x) * 4, y: Math.floor(ctx.transformedPoint(lastX,lastY).y) * 4, }; let rgbColor = hexToRgb(currentColor); let pixel = JSON.stringify({ "x": `${point.x/4}`, "y": `${point.y/4}`, "r": `${rgbColor.r}`, "g": `${rgbColor.g}`, "b": `${rgbColor.b}` }); let packet = { "code": 1, "content": pixel } socket.send(JSON.stringify(packet)); // console.log(`{\"code\":1, \"content\":${pixel}}`) // drawPixel(point.x, point.y, undefined); } },false); var scaleFactor = 1.1; var zoom = function(clicks){ var pt = ctx.transformedPoint(lastX,lastY); ctx.translate(pt.x,pt.y); var factor = Math.pow(scaleFactor,clicks); ctx.scale(factor,factor); ctx.translate(-pt.x,-pt.y); redraw(); } var handleScroll = function(evt){ var delta = evt.wheelDelta ? evt.wheelDelta/40 : evt.detail ? -evt.detail : 0; if (delta) zoom(delta); return evt.preventDefault() && false; }; canvas.addEventListener('DOMMouseScroll',handleScroll,false); canvas.addEventListener('mousewheel',handleScroll,false); }; function evaulatePixelNumber(x, y) { let pixelNumber; if (y > 0) pixelNumber = (y) * canvas.width + x if (y == 0) pixelNumber = x return pixelNumber } function changeColor(color) { if (color == undefined) currentColor = colorInput.value; else { currentColor = color colorInput.value = color } } //Code from https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb#5624139 function hexToRgb(hex) { // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; hex = hex.replace(shorthandRegex, function(m, r, g, b) { return r + r + g + g + b + b; }); var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null; } const rgbToHex = (r, g, b) => '#' + [r, g, b].map(x => { const hex = x.toString(16) return hex.length === 1 ? '0' + hex : hex }).join('') function trackTransforms(ctx){ var svg = document.createElementNS("http://www.w3.org/2000/svg",'svg'); var xform = svg.createSVGMatrix(); ctx.getTransform = function(){ return xform; }; var savedTransforms = []; var save = ctx.save; ctx.save = function(){ savedTransforms.push(xform.translate(0,0)); return save.call(ctx); }; var restore = ctx.restore; ctx.restore = function(){ xform = savedTransforms.pop(); return restore.call(ctx); }; var scale = ctx.scale; ctx.scale = function(sx,sy){ xform = xform.scaleNonUniform(sx,sy); return scale.call(ctx,sx,sy); }; var rotate = ctx.rotate; ctx.rotate = function(radians){ xform = xform.rotate(radians*180/Math.PI); return rotate.call(ctx,radians); }; var translate = ctx.translate; ctx.translate = function(dx,dy){ xform = xform.translate(dx,dy); return translate.call(ctx,dx,dy); }; var transform = ctx.transform; ctx.transform = function(a,b,c,d,e,f){ var m2 = svg.createSVGMatrix(); m2.a=a; m2.b=b; m2.c=c; m2.d=d; m2.e=e; m2.f=f; xform = xform.multiply(m2); return transform.call(ctx,a,b,c,d,e,f); }; var setTransform = ctx.setTransform; ctx.setTransform = function(a,b,c,d,e,f){ xform.a = a; xform.b = b; xform.c = c; xform.d = d; xform.e = e; xform.f = f; return setTransform.call(ctx,a,b,c,d,e,f); }; var pt = svg.createSVGPoint(); ctx.transformedPoint = function(x,y){ pt.x=x; pt.y=y; return pt.matrixTransform(xform.inverse()); } }