Files
EnvelopeGenerator/EnvelopeGenerator.ReceiverUI/wwwroot/js/receiver-signature.js
TekH 0a22e4e5cc Enhance signature popup with multiple input modes
Refactored `@using` directives in `ReportViewer.razor` to use specific aliases for improved readability. Updated the signature popup to support three input modes: "Draw", "Text", and "Image", with a tabbed interface for switching between modes. Added font selection for typed signatures and improved state management for signature modes.

Refactored JavaScript interop methods to handle initialization, clearing, and data retrieval for each signature mode. Enhanced `receiver-signature.js` to support typed and image-based signatures, including rendering text-based signatures and handling image uploads with scaling and centering.

Improved maintainability by modularizing code and extracting reusable functions. Enhanced user experience with dynamic UI updates and better handling of signature state changes.
2026-05-28 14:39:38 +02:00

222 lines
6.9 KiB
JavaScript

window.receiverSignature = (() => {
const pads = new Map();
const typedSignatures = new Map();
const imageSignatures = new Map();
function getPosition(canvas, event) {
const rect = canvas.getBoundingClientRect();
const source = event.touches && event.touches.length ? event.touches[0] : event;
return {
x: (source.clientX - rect.left) * (canvas.width / rect.width),
y: (source.clientY - rect.top) * (canvas.height / rect.height)
};
}
function clearCanvas(canvas) {
canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
}
function initialize(canvasId) {
const canvas = document.getElementById(canvasId);
if (!canvas || pads.has(canvasId))
return;
const context = canvas.getContext('2d');
context.lineWidth = 2.5;
context.lineCap = 'round';
context.lineJoin = 'round';
context.strokeStyle = '#111';
const state = { drawing: false, hasSignature: false };
pads.set(canvasId, state);
const start = event => {
event.preventDefault();
const pos = getPosition(canvas, event);
state.drawing = true;
context.beginPath();
context.moveTo(pos.x, pos.y);
};
const move = event => {
if (!state.drawing)
return;
event.preventDefault();
const pos = getPosition(canvas, event);
context.lineTo(pos.x, pos.y);
context.stroke();
state.hasSignature = true;
};
const end = event => {
if (!state.drawing)
return;
event.preventDefault();
state.drawing = false;
};
canvas.addEventListener('mousedown', start);
canvas.addEventListener('mousemove', move);
window.addEventListener('mouseup', end);
canvas.addEventListener('touchstart', start, { passive: false });
canvas.addEventListener('touchmove', move, { passive: false });
canvas.addEventListener('touchend', end, { passive: false });
}
function initializeTyped(canvasId) {
const canvas = document.getElementById(canvasId);
if (!canvas || typedSignatures.has(canvasId))
return;
typedSignatures.set(canvasId, { hasSignature: false });
}
function initializeImage(inputId, canvasId) {
const input = document.getElementById(inputId);
const canvas = document.getElementById(canvasId);
if (!input || !canvas || imageSignatures.has(canvasId))
return;
const state = { hasSignature: false };
imageSignatures.set(canvasId, state);
input.addEventListener('change', () => {
const file = input.files && input.files.length ? input.files[0] : null;
if (!file || !file.type.startsWith('image/')) {
clearCanvas(canvas);
state.hasSignature = false;
return;
}
const reader = new FileReader();
reader.onload = () => {
const image = new Image();
image.onload = () => {
const context = canvas.getContext('2d');
clearCanvas(canvas);
const padding = 10;
const maxWidth = canvas.width - padding * 2;
const maxHeight = canvas.height - padding * 2;
const scale = Math.min(maxWidth / image.width, maxHeight / image.height, 1);
const width = image.width * scale;
const height = image.height * scale;
const x = (canvas.width - width) / 2;
const y = (canvas.height - height) / 2;
context.drawImage(image, x, y, width, height);
state.hasSignature = true;
};
image.src = reader.result;
};
reader.readAsDataURL(file);
});
}
function clear(canvasId) {
const canvas = document.getElementById(canvasId);
const state = pads.get(canvasId);
if (!canvas || !state)
return;
clearCanvas(canvas);
state.hasSignature = false;
}
function clearTyped(canvasId) {
const canvas = document.getElementById(canvasId);
const state = typedSignatures.get(canvasId);
if (!canvas || !state)
return;
clearCanvas(canvas);
state.hasSignature = false;
}
function clearImage(inputId, canvasId) {
const input = document.getElementById(inputId);
const canvas = document.getElementById(canvasId);
const state = imageSignatures.get(canvasId);
if (!canvas || !state)
return;
if (input)
input.value = '';
clearCanvas(canvas);
state.hasSignature = false;
}
function renderTypedSignature(canvasId, text, fontFamily) {
const canvas = document.getElementById(canvasId);
const state = typedSignatures.get(canvasId);
if (!canvas || !state)
return;
const value = (text || '').trim();
clearCanvas(canvas);
if (!value) {
state.hasSignature = false;
return;
}
const context = canvas.getContext('2d');
const maxWidth = canvas.width - 30;
let fontSize = 54;
do {
context.font = `italic ${fontSize}px ${fontFamily || 'cursive'}`;
fontSize -= 2;
} while (context.measureText(value).width > maxWidth && fontSize > 24);
context.fillStyle = '#111';
context.textBaseline = 'middle';
context.textAlign = 'center';
context.fillText(value, canvas.width / 2, canvas.height / 2);
state.hasSignature = true;
}
function getDataUrl(canvasId) {
const canvas = document.getElementById(canvasId);
const state = pads.get(canvasId);
if (!canvas || !state || !state.hasSignature)
return null;
return canvas.toDataURL('image/png');
}
function getTypedDataUrl(canvasId) {
const canvas = document.getElementById(canvasId);
const state = typedSignatures.get(canvasId);
if (!canvas || !state || !state.hasSignature)
return null;
return canvas.toDataURL('image/png');
}
function getImageDataUrl(canvasId) {
const canvas = document.getElementById(canvasId);
const state = imageSignatures.get(canvasId);
if (!canvas || !state || !state.hasSignature)
return null;
return canvas.toDataURL('image/png');
}
return {
initialize,
initializeTyped,
initializeImage,
clear,
clearTyped,
clearImage,
renderTypedSignature,
getDataUrl,
getTypedDataUrl,
getImageDataUrl
};
})();