|
5 | 5 | const canvas = document.getElementById('annotation-canvas'); |
6 | 6 | const ctx = canvas.getContext('2d'); |
7 | 7 |
|
8 | | - let currentTool = 'arrow'; // 'arrow', 'rectangle', or 'text' |
| 8 | + let currentTool = 'arrow'; // 'arrow', 'rectangle', 'text', or 'blur' |
9 | 9 | let isDrawing = false; |
10 | 10 | let isEditingText = false; |
11 | 11 | let startX, startY; |
|
18 | 18 | const LINE_WIDTH = 3; |
19 | 19 | const TEXT_FONT_SIZE = 20; |
20 | 20 | const TEXT_FONT = `bold ${TEXT_FONT_SIZE}px 'IBM Plex Sans', sans-serif`; |
| 21 | + const BLUR_RADIUS = 12; // px – strength of the blur effect |
21 | 22 |
|
22 | 23 | // Text input element |
23 | 24 | const textInput = document.getElementById('text-input'); |
|
84 | 85 | drawRectangle(annotation.startX, annotation.startY, annotation.width, annotation.height); |
85 | 86 | } else if (annotation.type === 'text') { |
86 | 87 | drawText(annotation.x, annotation.y, annotation.text); |
| 88 | + } else if (annotation.type === 'blur') { |
| 89 | + drawBlur(annotation.x, annotation.y, annotation.width, annotation.height); |
87 | 90 | } |
88 | 91 | } |
89 | 92 |
|
|
120 | 123 | ctx.stroke(); |
121 | 124 | } |
122 | 125 |
|
| 126 | + // Draw blurred region |
| 127 | + function drawBlur(x, y, width, height) { |
| 128 | + if (!baseImage || Math.abs(width) < 2 || Math.abs(height) < 2) return; |
| 129 | + |
| 130 | + // Normalize so rect always starts at top-left |
| 131 | + const rx = width < 0 ? x + width : x; |
| 132 | + const ry = height < 0 ? y + height : y; |
| 133 | + const rw = Math.abs(width); |
| 134 | + const rh = Math.abs(height); |
| 135 | + |
| 136 | + ctx.save(); |
| 137 | + ctx.beginPath(); |
| 138 | + ctx.rect(rx, ry, rw, rh); |
| 139 | + ctx.clip(); // confine blur to the selected rectangle |
| 140 | + ctx.filter = `blur(${BLUR_RADIUS}px)`; |
| 141 | + // Redraw the base image with blur; clip prevents it bleeding outside the rect |
| 142 | + ctx.drawImage(baseImage, 0, 0); |
| 143 | + ctx.restore(); |
| 144 | + } |
| 145 | + |
123 | 146 | // Draw text |
124 | 147 | function drawText(x, y, text) { |
125 | 148 | ctx.font = TEXT_FONT; |
|
231 | 254 | const width = pos.x - startX; |
232 | 255 | const height = pos.y - startY; |
233 | 256 | drawRectangle(startX, startY, width, height); |
| 257 | + } else if (currentTool === 'blur') { |
| 258 | + const width = pos.x - startX; |
| 259 | + const height = pos.y - startY; |
| 260 | + drawBlur(startX, startY, width, height); |
234 | 261 | } |
235 | 262 | }); |
236 | 263 |
|
|
257 | 284 | width: pos.x - startX, |
258 | 285 | height: pos.y - startY |
259 | 286 | }); |
| 287 | + } else if (currentTool === 'blur') { |
| 288 | + const width = pos.x - startX; |
| 289 | + const height = pos.y - startY; |
| 290 | + if (Math.abs(width) >= 2 && Math.abs(height) >= 2) { |
| 291 | + annotations.push({ |
| 292 | + type: 'blur', |
| 293 | + x: startX, |
| 294 | + y: startY, |
| 295 | + width: width, |
| 296 | + height: height |
| 297 | + }); |
| 298 | + } |
260 | 299 | } |
261 | 300 |
|
262 | 301 | redrawCanvas(); |
|
270 | 309 | document.getElementById('arrow-tool').classList.toggle('active', tool === 'arrow'); |
271 | 310 | document.getElementById('rectangle-tool').classList.toggle('active', tool === 'rectangle'); |
272 | 311 | document.getElementById('text-tool').classList.toggle('active', tool === 'text'); |
| 312 | + document.getElementById('blur-tool').classList.toggle('active', tool === 'blur'); |
273 | 313 | canvas.className = 'tool-' + tool; |
274 | 314 | } |
275 | 315 |
|
276 | 316 | document.getElementById('arrow-tool').addEventListener('click', () => setActiveTool('arrow')); |
277 | 317 | document.getElementById('rectangle-tool').addEventListener('click', () => setActiveTool('rectangle')); |
278 | 318 | document.getElementById('text-tool').addEventListener('click', () => setActiveTool('text')); |
| 319 | + document.getElementById('blur-tool').addEventListener('click', () => setActiveTool('blur')); |
279 | 320 |
|
280 | 321 | // Undo button |
281 | 322 | document.getElementById('undo-button').addEventListener('click', undo); |
|
328 | 369 | document.getElementById('rectangle-tool').click(); |
329 | 370 | } else if (e.key === 't' || e.key === 'T') { |
330 | 371 | document.getElementById('text-tool').click(); |
| 372 | + } else if (e.key === 'b' || e.key === 'B') { |
| 373 | + document.getElementById('blur-tool').click(); |
331 | 374 | } else if (e.key === 'z' && (e.ctrlKey || e.metaKey)) { |
332 | 375 | e.preventDefault(); |
333 | 376 | undo(); |
|
0 commit comments