diff --git a/html/js/paint.js b/html/js/paint.js
index e28b94b..1a8f2e4 100644
--- a/html/js/paint.js
+++ b/html/js/paint.js
@@ -19,6 +19,9 @@ class PaintManager {
this.textBold = false;
this.textItalic = false;
+ // Brush cursor indicator
+ this.brushCursor = null;
+
// Undo/Redo functionality
this.historyStack = [];
this.historyStep = -1;
@@ -33,6 +36,8 @@ class PaintManager {
this.onTouchMove = this.onTouchMove.bind(this);
this.onTouchEnd = this.onTouchEnd.bind(this);
this.handleKeyboard = this.handleKeyboard.bind(this);
+ this.updateBrushCursor = this.updateBrushCursor.bind(this);
+ this.hideBrushCursor = this.hideBrushCursor.bind(this);
}
saveToHistory() {
@@ -132,8 +137,9 @@ class PaintManager {
this.brushColor = e.target.value;
});
- document.getElementById('brush-size').addEventListener('change', (e) => {
+ document.getElementById('brush-size').addEventListener('input', (e) => {
this.brushSize = parseInt(e.target.value);
+ this.updateBrushCursorSize();
});
document.getElementById('add-text-btn').addEventListener('click', () => this.startTextPlacement());
@@ -167,6 +173,14 @@ class PaintManager {
// Keyboard shortcuts for undo/redo
document.addEventListener('keydown', this.handleKeyboard);
+ // Mouse move for brush cursor
+ this.canvas.addEventListener('mousemove', this.updateBrushCursor);
+ this.canvas.addEventListener('mouseenter', this.updateBrushCursor);
+ this.canvas.addEventListener('mouseleave', this.hideBrushCursor);
+
+ // Create brush cursor element
+ this.createBrushCursor();
+
// Initialize history with blank canvas state
this.saveToHistory();
}
@@ -204,6 +218,104 @@ class PaintManager {
// Cancel any pending text placement
this.cancelTextPlacement();
+
+ // Update brush cursor visibility
+ this.updateBrushCursorVisibility();
+ }
+
+ createBrushCursor() {
+ // Create a div element to show as brush cursor
+ this.brushCursor = document.createElement('div');
+ this.brushCursor.id = 'brush-cursor';
+ this.brushCursor.style.position = 'fixed';
+ this.brushCursor.style.border = '2px solid rgba(0, 0, 0, 0.5)';
+ this.brushCursor.style.borderRadius = '50%';
+ this.brushCursor.style.pointerEvents = 'none';
+ this.brushCursor.style.display = 'none';
+ this.brushCursor.style.zIndex = '10000';
+ this.brushCursor.style.transform = 'translate(-50%, -50%)';
+ this.brushCursor.style.willChange = 'transform';
+ this.brushCursor.style.left = '0';
+ this.brushCursor.style.top = '0';
+ document.body.appendChild(this.brushCursor);
+ this.updateBrushCursorSize();
+
+ // For requestAnimationFrame throttling
+ this.cursorUpdateScheduled = false;
+ this.pendingCursorX = 0;
+ this.pendingCursorY = 0;
+ }
+
+ updateBrushCursorSize() {
+ if (!this.brushCursor) return;
+
+ const rect = this.canvas.getBoundingClientRect();
+ const scaleX = rect.width / this.canvas.width;
+ const scaleY = rect.height / this.canvas.height;
+ const scale = Math.min(scaleX, scaleY);
+
+ const size = this.brushSize * scale;
+ this.brushCursor.style.width = size + 'px';
+ this.brushCursor.style.height = size + 'px';
+ }
+
+ updateBrushCursorVisibility() {
+ if (!this.brushCursor) return;
+
+ if (this.currentTool === 'brush' || this.currentTool === 'eraser') {
+ this.brushCursor.style.display = 'block';
+ this.canvas.style.cursor = 'none';
+ } else {
+ this.brushCursor.style.display = 'none';
+ this.canvas.style.cursor = 'default';
+ }
+ }
+
+ updateBrushCursor(e) {
+ if (!this.brushCursor) return;
+
+ if (this.currentTool === 'brush' || this.currentTool === 'eraser') {
+ this.brushCursor.style.display = 'block';
+ this.canvas.style.cursor = 'none';
+
+ // Store the pending position
+ this.pendingCursorX = e.clientX;
+ this.pendingCursorY = e.clientY;
+
+ // Schedule update using requestAnimationFrame for smooth movement
+ if (!this.cursorUpdateScheduled) {
+ this.cursorUpdateScheduled = true;
+ requestAnimationFrame(() => {
+ this.brushCursor.style.transform = `translate(${this.pendingCursorX}px, ${this.pendingCursorY}px) translate(-50%, -50%)`;
+ this.cursorUpdateScheduled = false;
+ });
+ }
+
+ // Update color to match brush or show white for eraser (only needs to happen once or when tool changes)
+ if (this.currentTool === 'eraser') {
+ if (this.brushCursor.getAttribute('data-tool') !== 'eraser') {
+ this.brushCursor.style.border = '2px solid rgba(255, 0, 0, 0.7)';
+ this.brushCursor.style.backgroundColor = 'rgba(255, 255, 255, 0.2)';
+ this.brushCursor.style.boxShadow = 'none';
+ this.brushCursor.setAttribute('data-tool', 'eraser');
+ }
+ } else {
+ if (this.brushCursor.getAttribute('data-tool') !== 'brush') {
+ // Use a contrasting border - white with black outline for visibility
+ this.brushCursor.style.border = '1px solid white';
+ this.brushCursor.style.boxShadow = '0 0 0 1px black, inset 0 0 0 1px black';
+ this.brushCursor.style.backgroundColor = 'transparent';
+ this.brushCursor.setAttribute('data-tool', 'brush');
+ }
+ }
+ }
+ }
+
+ hideBrushCursor() {
+ if (this.brushCursor) {
+ this.brushCursor.style.display = 'none';
+ }
+ this.canvas.style.cursor = 'default';
}
startPaint(e) {