function startPainter(mac, width, height) { let isDrawing = false; let lastX = 0; let lastY = 0; let color = 'black'; let linewidth = 3; let cursor = 'auto'; let isAddingText = false; let layerDiv, intervalId, showCursor, input, textX, textY, font, sizeSelect, isDragging; var fonts = ['Roboto', 'Open Sans', 'Lato', 'Montserrat', 'PT Sans', 'Barlow Condensed', 'Headland One', 'Sofia Sans Extra Condensed', 'Mynerve', 'Lilita One', 'Passion One', 'Big Shoulders Display']; loadGoogleFonts(fonts); const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); ctx.fillStyle = 'white'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.imageSmoothingEnabled = false; $("#canvasdiv").appendChild(canvas); canvas.style.imageRendering = 'pixelated'; canvas.addEventListener('mousedown', startDrawing); window.addEventListener('mouseup', stopDrawing); canvas.addEventListener('mousemove', draw); canvas.addEventListener('touchstart', startDrawing, { passive: false }); window.addEventListener('touchend', stopDrawing); canvas.addEventListener('touchmove', draw, { passive: false }); const bgCanvas = document.createElement('canvas'); bgCanvas.width = canvas.width; bgCanvas.height = canvas.height; const bgCtx = bgCanvas.getContext('2d'); bgCtx.fillStyle = '#ffffff'; bgCtx.fillRect(0, 0, bgCanvas.width, bgCanvas.height); const txtButton = document.createElement('button'); txtButton.innerHTML = 'tT'; txtButton.style.fontStyle = 'italic'; txtButton.addEventListener('click', addText); const blackButton = document.createElement('button'); blackButton.innerHTML = '﹏🖌'; blackButton.style.color = 'black'; blackButton.addEventListener('click', () => { color = 'black'; linewidth = 3; cursor = 'url("data:image/svg+xml;utf8,") 2 2, auto'; blackButton.classList.add('active'); redButton.classList.remove('active'); whiteButton.classList.remove('active'); }); blackButton.classList.add('active'); const redButton = document.createElement('button'); redButton.innerHTML = '﹏🖌'; redButton.style.color = 'red'; redButton.addEventListener('click', () => { color = 'red'; linewidth = 3; cursor = 'url("data:image/svg+xml;utf8,") 2 2, auto'; blackButton.classList.remove('active'); redButton.classList.add('active'); whiteButton.classList.remove('active'); }); const whiteButton = document.createElement('button'); whiteButton.innerHTML = '⬤'; whiteButton.style.color = 'white'; whiteButton.addEventListener('click', () => { color = 'white'; linewidth = 20; cursor = 'url("data:image/svg+xml;utf8,") 10 10, auto'; blackButton.classList.remove('active'); redButton.classList.remove('active'); whiteButton.classList.add('active'); }); const clearButton = document.createElement('button'); clearButton.innerHTML = '🖵'; clearButton.addEventListener('click', () => { if (isAddingText) handleFinish(false); ctx.fillStyle = 'white'; ctx.fillRect(0, 0, canvas.width, canvas.height); }); const uploadButton = document.createElement('button'); uploadButton.innerHTML = 'Upload'; uploadButton.addEventListener('click', () => { if (isAddingText) handleFinish(true); const dataURL = canvas.toDataURL('image/jpeg'); const binaryImage = dataURLToBlob(dataURL); const formData = new FormData(); formData.append('mac', mac); formData.append('dither', '0'); formData.append('file', binaryImage, 'image.jpg'); const xhr = new XMLHttpRequest(); xhr.open('POST', '/imgupload'); xhr.send(formData); $('#configbox').style.display = 'none'; }); $("#buttonbar").appendChild(blackButton); $("#buttonbar").appendChild(redButton); $("#buttonbar").appendChild(whiteButton); $("#buttonbar").appendChild(txtButton); $("#buttonbar").appendChild(clearButton); $("#savebar").appendChild(uploadButton); canvas.addEventListener('mouseenter', function () { if (!isAddingText) { canvas.style.cursor = cursor; } else { canvas.style.cursor = 'move'; } }); canvas.addEventListener('mouseleave', function () { canvas.style.cursor = 'auto'; }); function startDrawing(e) { e.stopPropagation(); e.preventDefault(); if (isAddingText) return; isDrawing = true; var rect = canvas.getBoundingClientRect(); lastX = e.pageX - rect.left - window.scrollX; lastY = e.pageY - rect.top - window.scrollY; } function stopDrawing(e) { if (isAddingText) return; isDrawing = false; } function draw(e) { e.stopPropagation(); e.preventDefault(); if (isAddingText) return; if (!isDrawing) return; var rect = canvas.getBoundingClientRect(); ctx.beginPath(); ctx.moveTo(lastX, lastY); ctx.lineTo(e.pageX - rect.left - window.scrollX, e.pageY - rect.top - window.scrollY); ctx.strokeStyle = color; ctx.lineWidth = linewidth; ctx.lineCap = "round"; ctx.stroke(); lastX = e.pageX - rect.left - window.scrollX; lastY = e.pageY - rect.top - window.scrollY; } function addText() { if (isAddingText) { handleFinish(true); return; } txtButton.classList.add('active'); bgCtx.drawImage(canvas, 0, 0); const defaultX = 5; const defaultY = 40; isDragging = false; let startX, startY; showCursor = true; textX = defaultX; textY = defaultY; font = '24px ' + fonts[0]; input = document.createElement('textarea'); input.type = 'text'; input.placeholder = 'Type text here'; input.style.opacity = '0'; input.style.position = 'absolute'; input.style.left = '-200px' input.addEventListener('input', () => { drawText(input.value, textX, textY); }); input.addEventListener('keyup', () => { input.selectionStart = input.selectionEnd = input.value.length; }); input.addEventListener('blur', function () { input.focus(); }); intervalId = setInterval(function () { showCursor = !showCursor; drawText(input.value, textX, textY); }, 300); canvas.addEventListener('mouseup', handleMouseUp); canvas.addEventListener('mousedown', handleMouseDown); canvas.addEventListener('mousemove', handleMouseMove); canvas.addEventListener('touchstart', handleTouchStart, { passive: true }); canvas.addEventListener('touchend', handleTouchEnd); canvas.addEventListener('touchmove', handleTouchMove, { passive: true }); var sizes = [10,11,12,13,14,16,18,20,24,28,32,36,40,48,56,64,72,84]; const fontSelect = document.createElement('select'); fontSelect.id = 'font-select'; for (var i = 0; i < fonts.length; i++) { const option = document.createElement('option'); option.value = fonts[i]; option.text = fonts[i]; option.style.fontFamily = fonts[i]; fontSelect.appendChild(option); } sizeSelect = document.createElement('select'); sizeSelect.id = 'size-select'; for (var i = 0; i < sizes.length; i++) { const option = document.createElement('option'); option.value = sizes[i]; option.text = sizes[i] + ' px'; sizeSelect.appendChild(option); } function updateFont() { var selectedFont = fontSelect.value; var selectedSize = sizeSelect.value; fontSelect.style.fontFamily = selectedFont; font = selectedSize + 'px ' + selectedFont; drawText(input.value, textX, textY); } fontSelect.value = fonts[0]; sizeSelect.value = '24'; fontSelect.addEventListener('change', updateFont); sizeSelect.addEventListener('change', updateFont); const finishButton = document.createElement('button'); finishButton.innerHTML = '✔'; finishButton.addEventListener('click', clickHandleFinish); layerDiv = document.createElement('div'); layerDiv.appendChild(input); layerDiv.appendChild(fontSelect); layerDiv.appendChild(sizeSelect); layerDiv.appendChild(finishButton); $("#layersdiv").appendChild(layerDiv); input.focus(); isAddingText = true; //cursor = 'move'; blackButton.innerHTML = 'aA' redButton.innerHTML = 'aA' if (color=='white') { whiteButton.classList.remove('active'); blackButton.classList.add('active'); color='black'; } } function handleFinish(apply) { canvas.removeEventListener('mousedown', handleMouseDown); canvas.removeEventListener('mouseup', handleMouseUp); canvas.removeEventListener('mousemove', handleMouseMove); canvas.removeEventListener('touchstart', handleTouchStart); canvas.removeEventListener('touchend', handleTouchEnd); canvas.removeEventListener('touchmove', handleTouchMove); isAddingText = false; cursor = 'auto'; layerDiv.remove(); clearInterval(intervalId); showCursor = false; if (apply) drawText(input.value, textX, textY); txtButton.classList.remove('active'); blackButton.innerHTML = '﹏🖌' redButton.innerHTML = '﹏🖌' } function drawText(text, x, y) { ctx.drawImage(bgCanvas, 0, 0); ctx.save(); ctx.translate(x, y); ctx.font = font; ctx.fillStyle = color; const lines = text.split('\n'); lines.forEach((line, index) => { ctx.fillText(line + (showCursor && index === lines.length - 1 ? '|' : ''), 0, index * (sizeSelect.value * 1.1)); }); ctx.restore(); } function handleMouseDown(e) { isDragging = true; startX = textX; startY = textY; ({ clientX: lastMouseX, clientY: lastMouseY } = e); } function handleMouseMove(e) { if (isDragging) { const { clientX, clientY } = e; textX = startX + clientX - lastMouseX; textY = startY + clientY - lastMouseY; drawText(input.value, textX, textY); } } function handleTouchStart(e) { isDragging = true; startX = textX; startY = textY; ({ clientX: lastTouchX, clientY: lastTouchY } = e.touches[0]); } function handleTouchMove(e) { if (isDragging) { const { clientX, clientY } = e.touches[0]; textX = startX + clientX - lastTouchX; textY = startY + clientY - lastTouchY; drawText(input.value, textX, textY); } } function handleMouseUp(e) { isDragging = false; } function handleTouchEnd(e) { isDragging = false; } function clickHandleFinish() { handleFinish(true); } } function loadGoogleFonts(fonts) { var link = document.createElement('link'); link.rel = 'stylesheet'; link.href = 'https://fonts.googleapis.com/css?family=' + fonts.join('|'); document.head.appendChild(link); } function dataURLToBlob(dataURL) { const byteString = atob(dataURL.split(',')[1]); const mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0]; const arrayBuffer = new ArrayBuffer(byteString.length); const uint8Array = new Uint8Array(arrayBuffer); for (let i = 0; i < byteString.length; i++) { uint8Array[i] = byteString.charCodeAt(i); } return new Blob([arrayBuffer], { type: mimeString }); } paintLoaded = true;