| | |
| | |
| | |
| | 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 |