| |
| |
| |
| import param |
| from panel.reactive import ReactiveHTML |
|
|
|
|
| |
| |
| |
| class Canvas(ReactiveHTML): |
| ''' |
| The HTML canvas panel used for drawing digits (0-9) in the application. |
| Reference: https://panel.holoviz.org/how_to/custom_components/examples/canvas_draw.html |
| ''' |
| uri = param.String() |
| clear = param.Boolean(default = False) |
| |
| _template = ''' |
| <canvas |
| id="canvas" |
| style="width: 100%; height: 100%" |
| height=400px |
| width=400px |
| onmousedown="${script('start')}" |
| onmousemove="${script('draw')}" |
| onmouseup="${script('end')}" |
| onmouseleave="${script('end')}"> |
| </canvas> |
| ''' |
| |
| _scripts = { |
| 'render': ''' |
| state.ctx = canvas.getContext('2d'); |
| state.ctx.fillStyle = '#FFFFFF'; |
| state.ctx.fillRect(0, 0, canvas.width, canvas.height); |
| state.ctx.lineWidth = 30; |
| state.ctx.strokeStyle = '#000000'; |
| state.ctx.lineJoin = 'round'; |
| state.ctx.lineCap = 'round'; |
| |
| // Helper to normalize mouse coordinates |
| state.getCoords = function(e) { |
| const rect = canvas.getBoundingClientRect(); |
| return { |
| x: (e.clientX - rect.left) * (canvas.width / rect.width), |
| y: (e.clientY - rect.top) * (canvas.height / rect.height) |
| }; |
| }; |
| ''', |
| |
| 'start': ''' |
| if (state.isDrawing) return; |
| state.isDrawing = true; |
| const pos = state.getCoords(event); |
| state.ctx.beginPath(); |
| state.ctx.moveTo(pos.x, pos.y); |
| ''', |
| |
| 'draw': ''' |
| if (!state.isDrawing) return; |
| const pos = state.getCoords(event); |
| state.ctx.lineTo(pos.x, pos.y); |
| state.ctx.stroke(); |
| data.uri = canvas.toDataURL('image/png'); |
| ''', |
| |
| 'end': ''' |
| if (!state.isDrawing) return; // Early return if already not drawing |
| state.isDrawing = false; |
| ''', |
| |
| 'clear': ''' |
| state.ctx.fillStyle = '#FFFFFF'; |
| state.ctx.fillRect(0, 0, canvas.width, canvas.height); |
| data.uri = ''; |
| ''' |
| } |
|
|
| def toggle_clear(self, *event): |
| ''' |
| Toggles the value of self.clear to trigger the JS 'clear' function. |
| ''' |
| self.clear = not self.clear |