From 48e349c1d287707922572d6bc65def2491a55245 Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Tue, 20 Jan 2026 17:21:14 +0200 Subject: [PATCH] fix signature pad when resize --- app/javascript/draw.js | 51 +++++++++++++-- app/javascript/elements/signature_form.js | 53 +++++++++++++-- .../submission_form/signature_step.vue | 65 ++++++++++++++----- 3 files changed, 142 insertions(+), 27 deletions(-) diff --git a/app/javascript/draw.js b/app/javascript/draw.js index a4936e8d..f8d7ca19 100644 --- a/app/javascript/draw.js +++ b/app/javascript/draw.js @@ -4,14 +4,21 @@ import { isValidSignatureCanvas } from './submission_form/validate_signature' window.customElements.define('draw-signature', class extends HTMLElement { connectedCallback () { - const scale = 3 + this.setCanvasSize() - this.canvas.width = this.canvas.parentNode.clientWidth * scale - this.canvas.height = this.canvas.parentNode.clientHeight * scale + this.pad = new SignaturePad(this.canvas) - this.canvas.getContext('2d').scale(scale, scale) + this.resizeObserver = new ResizeObserver(() => { + const { width, height } = this.canvas - this.pad = new SignaturePad(this.canvas) + this.setCanvasSize() + + if (this.canvas.width !== width || this.canvas.height !== height) { + this.redrawCanvas(width, height) + } + }) + + this.resizeObserver.observe(this.canvas.parentNode) if (this.dataset.color) { this.pad.penColor = this.dataset.color @@ -57,6 +64,40 @@ window.customElements.define('draw-signature', class extends HTMLElement { this.updateSubmitButtonVisibility() } + disconnectedCallback () { + if (this.resizeObserver) { + this.resizeObserver.disconnect() + } + } + + setCanvasSize () { + const scale = 3 + + const width = this.canvas.parentNode.clientWidth + const height = this.canvas.parentNode.clientHeight + + if (this.canvas.width !== width * scale || this.canvas.height !== height * scale) { + this.canvas.width = width * scale + this.canvas.height = height * scale + + this.canvas.getContext('2d').scale(scale, scale) + } + } + + redrawCanvas (oldWidth, oldHeight) { + if (this.pad && !this.pad.isEmpty()) { + const sx = this.canvas.width / oldWidth + const sy = this.canvas.height / oldHeight + + const scaledData = this.pad.toData().map((stroke) => ({ + ...stroke, + points: stroke.points.map((p) => ({ ...p, x: p.x * sx, y: p.y * sy })) + })) + + this.pad.fromData(scaledData) + } + } + updateSubmitButtonVisibility () { if (this.pad.isEmpty()) { this.submitButton.style.display = 'none' diff --git a/app/javascript/elements/signature_form.js b/app/javascript/elements/signature_form.js index 55020081..d4305adc 100644 --- a/app/javascript/elements/signature_form.js +++ b/app/javascript/elements/signature_form.js @@ -5,16 +5,23 @@ export default targetable(class extends HTMLElement { static [target.static] = ['canvas', 'input', 'clear', 'button'] async connectedCallback () { - const scale = 3 + const { default: SignaturePad } = await import('signature_pad') + + this.setCanvasSize() - this.canvas.width = this.canvas.parentNode.clientWidth * scale - this.canvas.height = this.canvas.parentNode.clientHeight * scale + this.pad = new SignaturePad(this.canvas) - this.canvas.getContext('2d').scale(scale, scale) + this.resizeObserver = new ResizeObserver(() => { + const { width, height } = this.canvas - const { default: SignaturePad } = await import('signature_pad') + this.setCanvasSize() - this.pad = new SignaturePad(this.canvas) + if (this.canvas.width !== width || this.canvas.height !== height) { + this.redrawCanvas(width, height) + } + }) + + this.resizeObserver.observe(this.canvas.parentNode) this.clear.addEventListener('click', (e) => { e.preventDefault() @@ -47,4 +54,38 @@ export default targetable(class extends HTMLElement { this.closest('form').requestSubmit() } + + disconnectedCallback () { + if (this.resizeObserver) { + this.resizeObserver.disconnect() + } + } + + setCanvasSize () { + const scale = 3 + + const width = this.canvas.parentNode.clientWidth + const height = this.canvas.parentNode.clientWidth / 2.5 + + if (this.canvas.width !== width * scale || this.canvas.height !== height * scale) { + this.canvas.width = width * scale + this.canvas.height = height * scale + + this.canvas.getContext('2d').scale(scale, scale) + } + } + + redrawCanvas (oldWidth, oldHeight) { + if (this.pad && !this.pad.isEmpty()) { + const sx = this.canvas.width / oldWidth + const sy = this.canvas.height / oldHeight + + const scaledData = this.pad.toData().map((stroke) => ({ + ...stroke, + points: stroke.points.map((p) => ({ ...p, x: p.x * sx, y: p.y * sy })) + })) + + this.pad.fromData(scaledData) + } + } }) diff --git a/app/javascript/submission_form/signature_step.vue b/app/javascript/submission_form/signature_step.vue index 268cb6a9..d5726b67 100644 --- a/app/javascript/submission_form/signature_step.vue +++ b/app/javascript/submission_form/signature_step.vue @@ -175,9 +175,7 @@ v-if="isShowQr" class="top-0 bottom-0 right-0 left-0 absolute bg-base-content/10 rounded-2xl" > -
+
{ - if (this.$refs.canvas) { - this.$refs.canvas.width = this.$refs.canvas.parentNode.clientWidth * scale - this.$refs.canvas.height = this.$refs.canvas.parentNode.clientWidth * scale / 3 - - this.$refs.canvas.getContext('2d').scale(scale, scale) - } - }) + this.$nextTick(() => this.setCanvasSize()) if (this.$refs.canvas) { this.pad = new SignaturePad(this.$refs.canvas) @@ -470,13 +461,10 @@ export default { this.$emit('start') }) - this.intersectionObserver = new IntersectionObserver((entries, observer) => { + this.intersectionObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { - this.$refs.canvas.width = this.$refs.canvas.parentNode.clientWidth * scale - this.$refs.canvas.height = this.$refs.canvas.parentNode.clientWidth * scale / 3 - - this.$refs.canvas.getContext('2d').scale(scale, scale) + this.setCanvasSize() this.intersectionObserver?.disconnect() } @@ -484,13 +472,58 @@ export default { }) this.intersectionObserver.observe(this.$refs.canvas) + + this.resizeObserver = new ResizeObserver(() => { + const { width, height } = this.$refs.canvas + + this.setCanvasSize() + + if (this.$refs.canvas.width !== width || this.$refs.canvas.height !== height) { + this.redrawCanvas(width, height) + } + }) + + this.resizeObserver.observe(this.$refs.canvas.parentNode) } }, beforeUnmount () { this.intersectionObserver?.disconnect() + this.resizeObserver?.disconnect() this.stopCheckSignature() }, methods: { + setCanvasSize () { + const canvas = this.$refs.canvas + + if (canvas) { + const width = canvas.parentNode.clientWidth + const height = width / 3 + + if (canvas.width !== width * scale || canvas.height !== height * scale) { + canvas.width = width * scale + canvas.height = height * scale + + canvas.getContext('2d').scale(scale, scale) + } + } + }, + redrawCanvas (oldWidth, oldHeight) { + const canvas = this.$refs.canvas + + if (this.pad && !this.isTextSignature && !this.pad.isEmpty()) { + const sx = canvas.width / oldWidth + const sy = canvas.height / oldHeight + + const scaledData = this.pad.toData().map((stroke) => ({ + ...stroke, + points: stroke.points.map((p) => ({ ...p, x: p.x * sx, y: p.y * sy })) + })) + + this.pad.fromData(scaledData) + } else if (this.isTextSignature && this.$refs.textInput) { + this.updateWrittenSignature({ target: { value: this.$refs.textInput.value } }) + } + }, remove () { this.$emit('update:model-value', '')