Spaces:
Running
Running
class Whiteboard { | |
constructor() { | |
this.canvas = document.getElementById('whiteboard'); | |
this.ctx = this.canvas.getContext('2d'); | |
this.isDrawing = false; | |
this.history = new History(); | |
this.tools = new DrawingTools(this.canvas, this.ctx); | |
this.pages = new Pages(); | |
this.setupCanvas(); | |
this.setupEventListeners(); | |
this.setupBrushPreview(); | |
this.setupToolbarToggle(); | |
} | |
setupCanvas() { | |
const resize = () => { | |
this.canvas.width = window.innerWidth; | |
this.canvas.height = window.innerHeight; | |
this.setupDrawingStyle(); | |
// Restore the current page | |
const pageContent = this.pages.getPageContent(); | |
if (pageContent) { | |
this.ctx.putImageData(pageContent, 0, 0); // Use the drawing content from the current page | |
} | |
}; | |
window.addEventListener('resize', resize); | |
resize(); | |
} | |
setupToolbarToggle() { | |
const toolbar = document.querySelector('.bottom-toolbar'); | |
const toggleBtn = document.getElementById('toggleToolbar'); | |
toggleBtn.addEventListener('click', () => { | |
toolbar.classList.toggle('hidden'); | |
}); | |
} | |
setupBrushPreview() { | |
this.brushPreview = document.getElementById('brushPreview'); | |
this.canvas.addEventListener('mousemove', (e) => { | |
const { x, y } = getMousePos(this.canvas, e); | |
this.updateBrushPreview(x, y); | |
}); | |
this.canvas.addEventListener('mouseenter', () => { | |
this.brushPreview.style.display = 'block'; | |
}); | |
this.canvas.addEventListener('mouseleave', () => { | |
this.brushPreview.style.display = 'none'; | |
}); | |
} | |
updateBrushPreview(x, y) { | |
const size = this.tools.brushSize; | |
this.brushPreview.style.width = size + 'px'; | |
this.brushPreview.style.height = size + 'px'; | |
this.brushPreview.style.left = (x - size/2) + 'px'; | |
this.brushPreview.style.top = (y - size/2) + 'px'; | |
this.brushPreview.style.borderColor = this.tools.isEraser ? '#000' : this.tools.color; | |
} | |
setupDrawingStyle() { | |
this.ctx.lineCap = 'round'; | |
this.ctx.lineJoin = 'round'; | |
this.ctx.font = '24px Arial'; | |
this.tools.applyToolSettings(); | |
} | |
setupEventListeners() { | |
// Mouse events | |
this.canvas.addEventListener('mousedown', this.startDrawing.bind(this)); | |
this.canvas.addEventListener('mousemove', this.draw.bind(this)); | |
this.canvas.addEventListener('mouseup', this.stopDrawing.bind(this)); | |
this.canvas.addEventListener('mouseout', this.stopDrawing.bind(this)); | |
// Touch events | |
this.canvas.addEventListener('touchstart', this.handleTouch.bind(this)); | |
this.canvas.addEventListener('touchmove', this.handleTouch.bind(this)); | |
this.canvas.addEventListener('touchend', this.handleTouchEnd.bind(this)); | |
// Button events | |
document.getElementById('aiBtn').addEventListener('click', this.processDrawing.bind(this)); | |
document.getElementById('submitBtn').addEventListener('click', this.generatePDF.bind(this)); | |
document.getElementById('clearBtn').addEventListener('click', this.clearCanvas.bind(this)); | |
document.getElementById('undoBtn').addEventListener('click', this.undo.bind(this)); | |
document.getElementById('redoBtn').addEventListener('click', this.redo.bind(this)); | |
// Keyboard shortcuts | |
document.addEventListener('keydown', (e) => { | |
if (e.ctrlKey || e.metaKey) { | |
if (e.key === 'z') { | |
e.preventDefault(); | |
this.undo(); | |
} else if (e.key === 'y') { | |
e.preventDefault(); | |
this.redo(); | |
} | |
} | |
}); | |
} | |
handleTouch(e) { | |
e.preventDefault(); | |
const touch = e.touches[0]; | |
const eventType = e.type === 'touchstart' ? 'mousedown' : 'mousemove'; | |
const mouseEvent = new MouseEvent(eventType, { | |
clientX: touch.clientX, | |
clientY: touch.clientY | |
}); | |
this.canvas.dispatchEvent(mouseEvent); | |
} | |
handleTouchEnd(e) { | |
e.preventDefault(); | |
const mouseEvent = new MouseEvent('mouseup', {}); | |
this.canvas.dispatchEvent(mouseEvent); | |
} | |
startDrawing(e) { | |
this.isDrawing = true; | |
const { x, y } = getMousePos(this.canvas, e); | |
this.tools.applyToolSettings(); | |
this.ctx.beginPath(); | |
this.ctx.moveTo(x, y); | |
} | |
draw(e) { | |
if (!this.isDrawing) return; | |
const { x, y } = getMousePos(this.canvas, e); | |
this.ctx.lineTo(x, y); | |
this.ctx.stroke(); | |
} | |
stopDrawing() { | |
if (this.isDrawing) { | |
this.isDrawing = false; | |
this.saveState(); | |
} | |
} | |
saveState() { | |
const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height); | |
this.history.push(new DrawingState(imageData)); | |
this.pages.setPageContent(this.history.states); | |
} | |
undo() { | |
const state = this.history.undo(); | |
if (state) { | |
this.ctx.putImageData(state.imageData, 0, 0); | |
this.pages.setPageContent(this.history.states); | |
} | |
} | |
redo() { | |
const state = this.history.redo(); | |
if (state) { | |
this.ctx.putImageData(state.imageData, 0, 0); | |
this.pages.setPageContent(this.history.states); | |
} | |
} | |
clearCanvas() { | |
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
this.saveState(); | |
} | |
processDrawing() { | |
const tempCanvas = document.createElement('canvas'); | |
const tempContext = tempCanvas.getContext('2d'); | |
tempCanvas.width = this.canvas.width; | |
tempCanvas.height = this.canvas.height; | |
tempContext.fillStyle = 'white'; | |
tempContext.fillRect(0, 0, tempCanvas.width, tempCanvas.height); | |
tempContext.drawImage(this.canvas, 0, 0); | |
const imageData = tempCanvas.toDataURL('image/png'); | |
console.log('Processing drawing with AI...'); | |
console.log(imageData.substring(0, 100) + '...'); | |
fetch('/whitebai', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify({ | |
image: imageData | |
}) | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.status && Array.isArray(data.data)) { | |
data.data.forEach(item => { | |
const result = item.result; | |
const x = item.location.x; | |
const y = item.location.y; | |
const size = item.size; | |
const sizePx = `${size}px`; | |
writeOnCanvasDynamic(whiteboard.pages, result, x, y, null, "Arial", sizePx, "black", false); | |
}); | |
} else { | |
console.error("Invalid data object or empty data array"); | |
} | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
}); | |
} | |
async generatePDF() { | |
const pages = this.pages.getAllPages(); | |
for (let i = 0; i < pages.length; i++) { | |
const pageContent = pages[i]; | |
const tempCanvas = document.createElement('canvas'); | |
tempCanvas.width = this.canvas.width; | |
tempCanvas.height = this.canvas.height; | |
const tempCtx = tempCanvas.getContext('2d'); | |
tempCtx.fillStyle = 'white'; | |
tempCtx.fillRect(0, 0, tempCanvas.width, tempCanvas.height); | |
if (pageContent && pageContent.drawing) { | |
const imageData = pageContent.drawing; | |
if (imageData) { | |
try { | |
tempCtx.putImageData(imageData, 0, 0); | |
console.log("Image applied to canvas for page " + i); | |
} catch (error) { | |
console.log("Error putting image data on page " + i, error); | |
} | |
} else { | |
console.log("No image data for page " + i); | |
} | |
} | |
const imgData = tempCanvas.toDataURL('image/png'); | |
console.log(`Base64 data for page ${i}:`, imgData); | |
} | |
} | |
writeText(text, x = 20, y = 40) { | |
this.setupDrawingStyle(); | |
this.ctx.fillStyle = this.tools.color; | |
this.ctx.fillText(text.toString(), x, y); | |
this.saveState(); | |
} | |
} | |
const whiteboard = new Whiteboard(); | |
const canvas = document.getElementById('whiteboard'); | |
document.addEventListener('keydown', function(event) { | |
if (event.ctrlKey && event.key === 's') { | |
event.preventDefault(); | |
const tempCanvas = document.createElement('canvas'); | |
const tempContext = tempCanvas.getContext('2d'); | |
tempCanvas.width = canvas.width; | |
tempCanvas.height = canvas.height; | |
tempContext.fillStyle = 'white'; | |
tempContext.fillRect(0, 0, tempCanvas.width, tempCanvas.height); | |
tempContext.drawImage(canvas, 0, 0); | |
const imageData = tempCanvas.toDataURL("image/png"); | |
const link = document.createElement('a'); | |
link.href = imageData; | |
link.download = 'canvas-image.png'; | |
link.click(); | |
console.log("Canvas saved as image with white background!"); | |
} | |
}); | |
function writeOnCanvasDynamic(pagesInstance, text, x, y, fontUrl, fontFamily, fontSize = "16px", color = "black", clear = false) { | |
const ctx = pagesInstance.ctx; | |
if (!ctx) return; | |
const applyFontAndWrite = () => { | |
ctx.globalCompositeOperation = 'source-over'; | |
if (clear) { | |
const textWidth = ctx.measureText(text).width; | |
const lineHeight = parseInt(fontSize, 10) || 16; | |
ctx.clearRect(x, y - lineHeight, textWidth, lineHeight); | |
return; | |
} | |
ctx.font = `${fontSize} ${fontFamily}`; | |
ctx.fillStyle = color; | |
ctx.fillText(text, x, y); | |
}; | |
if (fontUrl) { | |
const fontFace = new FontFace(fontFamily, `url(${fontUrl})`); | |
fontFace.load().then((loadedFont) => { | |
document.fonts.add(loadedFont); | |
applyFontAndWrite(); | |
}).catch((err) => { | |
console.error("Font loading failed:", err); | |
}); | |
} else { | |
applyFontAndWrite(); | |
} | |
} | |