Improve signature scaling and responsiveness in PDF viewer
Reduced delay in `OnZoomChanged` to improve responsiveness when rendering signature buttons. Added calls to `RenderSignatureButtonsAsync` in zoom-related methods to ensure signature overlays update dynamically. Refactored `pdf-viewer.js` to introduce `appliedSignatureElements` for better management of applied signatures. Added `scaleAppliedSignature` and `updateAppliedSignaturePositions` methods to dynamically scale and position applied signatures based on zoom level and page. Enhanced signature button rendering by scaling dimensions (width, height, font size, icon size) proportionally with zoom. Added attributes to store base values for applied signature containers to facilitate scaling. Improved handling of applied signatures to ensure proper scaling, positioning, and visibility during zoom and page navigation. These changes enhance user experience and maintain consistency across zoom levels.
This commit is contained in:
@@ -628,8 +628,8 @@ const int MaxThumbnailWidth = 400;
|
||||
_currentZoom = (int)(scale * 100);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
// Re-render signature buttons when zoom changes
|
||||
await Task.Delay(100);
|
||||
// Small delay for canvas render to complete (reduced from 100ms to 10ms)
|
||||
await Task.Delay(10);
|
||||
await RenderSignatureButtonsAsync();
|
||||
}
|
||||
|
||||
@@ -652,6 +652,9 @@ const int MaxThumbnailWidth = 400;
|
||||
await JSRuntime.InvokeVoidAsync("pdfViewer.zoomIn");
|
||||
var scale = await JSRuntime.InvokeAsync<double>("pdfViewer.getScale");
|
||||
_currentZoom = (int)(scale * 100);
|
||||
|
||||
// Update signature overlay positions after zoom
|
||||
await RenderSignatureButtonsAsync();
|
||||
}
|
||||
|
||||
async Task ZoomOut() {
|
||||
@@ -659,6 +662,9 @@ const int MaxThumbnailWidth = 400;
|
||||
await JSRuntime.InvokeVoidAsync("pdfViewer.zoomOut");
|
||||
var scale = await JSRuntime.InvokeAsync<double>("pdfViewer.getScale");
|
||||
_currentZoom = (int)(scale * 100);
|
||||
|
||||
// Update signature overlay positions after zoom
|
||||
await RenderSignatureButtonsAsync();
|
||||
}
|
||||
|
||||
async Task SetZoom(int percentage) {
|
||||
@@ -670,6 +676,9 @@ const int MaxThumbnailWidth = 400;
|
||||
async Task OnZoomSliderChanged(ChangeEventArgs e) {
|
||||
if (int.TryParse(e.Value?.ToString(), out var zoom)) {
|
||||
await SetZoom(zoom);
|
||||
|
||||
// Update signature overlay positions after zoom
|
||||
await RenderSignatureButtonsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -309,16 +309,16 @@ window.pdfViewer = {
|
||||
return true;
|
||||
},
|
||||
|
||||
zoomIn() {
|
||||
async zoomIn() {
|
||||
const step = this.qualityOptions.zoomStepPercentage / 100;
|
||||
this.scale = Math.min(this.scale + step, 3.0);
|
||||
this.queueRenderPage(this.pageNum);
|
||||
await this.renderPage(this.pageNum);
|
||||
},
|
||||
|
||||
zoomOut() {
|
||||
async zoomOut() {
|
||||
const step = this.qualityOptions.zoomStepPercentage / 100;
|
||||
this.scale = Math.max(this.scale - step, 0.5);
|
||||
this.queueRenderPage(this.pageNum);
|
||||
await this.renderPage(this.pageNum);
|
||||
},
|
||||
|
||||
setScale(scale) {
|
||||
@@ -480,7 +480,8 @@ window.pdfViewer = {
|
||||
|
||||
// Signature button functionality
|
||||
signatureButtons: [],
|
||||
appliedSignatures: [], // Track which signatures have been applied
|
||||
appliedSignatures: [], // Track which signatures have been applied (ID list)
|
||||
appliedSignatureElements: [], // ✅ NEW: Track applied signature DOM elements
|
||||
_lastViewedSignatureId: null, // Track last viewed signature for navigation
|
||||
|
||||
/**
|
||||
@@ -765,12 +766,28 @@ window.pdfViewer = {
|
||||
signatureLayer.style.width = `${viewport.width / dpr}px`;
|
||||
signatureLayer.style.height = `${viewport.height / dpr}px`;
|
||||
|
||||
// Update applied signature coordinates for current zoom level
|
||||
this.updateAppliedSignaturePositions(signatureLayer, currentPageNum);
|
||||
|
||||
// Create button for each UNSIGNED signature
|
||||
pageSignatures.forEach(sig => {
|
||||
// Coordinates are in PDF POINTS - convert to display pixels
|
||||
const xPx = (sig.x * this.scale);
|
||||
const yPx = (sig.y * this.scale);
|
||||
|
||||
// ✅ FIXED: Scale button size proportionally with zoom
|
||||
const baseScale = 1.5; // Reference scale (initial load)
|
||||
const scaleFactor = this.scale / baseScale;
|
||||
const baseWidth = 150;
|
||||
const baseHeight = 60;
|
||||
const baseFontSize = 18;
|
||||
const baseIconSize = 24;
|
||||
|
||||
const scaledWidth = baseWidth * scaleFactor;
|
||||
const scaledHeight = baseHeight * scaleFactor;
|
||||
const scaledFontSize = Math.max(baseFontSize * scaleFactor, 10); // Min 10px
|
||||
const scaledIconSize = baseIconSize * scaleFactor;
|
||||
|
||||
// Create button element
|
||||
const button = document.createElement('button');
|
||||
button.className = 'signature-button';
|
||||
@@ -780,8 +797,8 @@ window.pdfViewer = {
|
||||
button.style.position = 'absolute';
|
||||
button.style.left = `${xPx}px`;
|
||||
button.style.top = `${yPx}px`;
|
||||
button.style.width = '150px';
|
||||
button.style.height = '60px';
|
||||
button.style.width = `${scaledWidth}px`; // ✅ Scaled
|
||||
button.style.height = `${scaledHeight}px`; // ✅ Scaled
|
||||
button.style.backgroundColor = '#4F46E5';
|
||||
button.style.color = 'white';
|
||||
button.style.border = 'none';
|
||||
@@ -801,14 +818,14 @@ window.pdfViewer = {
|
||||
// Add text
|
||||
const textDiv = document.createElement('div');
|
||||
textDiv.textContent = 'Unterschreiben';
|
||||
textDiv.style.fontSize = '18px';
|
||||
textDiv.style.fontSize = `${scaledFontSize}px`; // ✅ Scaled
|
||||
textDiv.style.fontWeight = '700';
|
||||
|
||||
// Add SVG icon
|
||||
const svgNS = 'http://www.w3.org/2000/svg';
|
||||
const svg = document.createElementNS(svgNS, 'svg');
|
||||
svg.setAttribute('width', '24');
|
||||
svg.setAttribute('height', '24');
|
||||
svg.setAttribute('width', scaledIconSize); // ✅ Scaled
|
||||
svg.setAttribute('height', scaledIconSize); // ✅ Scaled
|
||||
svg.setAttribute('viewBox', '0 8 32 36');
|
||||
svg.setAttribute('fill', 'none');
|
||||
svg.style.filter = 'drop-shadow(0 1px 2px rgba(0,0,0,0.2))';
|
||||
@@ -851,6 +868,81 @@ window.pdfViewer = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Scales an applied signature container based on current zoom level
|
||||
* @param {HTMLElement} container - Applied signature container
|
||||
* @param {number} currentScale - Current PDF zoom scale
|
||||
*/
|
||||
scaleAppliedSignature(container, currentScale) {
|
||||
const baseScale = 1.5; // Reference scale (initial load)
|
||||
const scaleFactor = currentScale / baseScale;
|
||||
|
||||
// Scale width
|
||||
const baseWidth = parseInt(container.getAttribute('data-base-width') || 230);
|
||||
container.style.width = `${baseWidth * scaleFactor}px`;
|
||||
|
||||
// Scale padding
|
||||
const basePadding = parseInt(container.getAttribute('data-base-padding') || 12);
|
||||
container.style.padding = `${basePadding * scaleFactor}px`;
|
||||
|
||||
// Scale border radius (subtle detail)
|
||||
const baseBorderRadius = parseInt(container.getAttribute('data-base-border-radius') || 6);
|
||||
container.style.borderRadius = `${baseBorderRadius * scaleFactor}px`;
|
||||
|
||||
// Scale font size (with min 6px for readability)
|
||||
const infoContainer = container.querySelector('.signature-info-text');
|
||||
if (infoContainer) {
|
||||
const baseFontSize = parseInt(infoContainer.getAttribute('data-base-font-size') || 9);
|
||||
const scaledFontSize = Math.max(baseFontSize * scaleFactor, 6);
|
||||
infoContainer.style.fontSize = `${scaledFontSize}px`;
|
||||
}
|
||||
|
||||
// Scale image max height
|
||||
const baseImgHeight = parseInt(container.getAttribute('data-base-img-height') || 70);
|
||||
const img = container.querySelector('img');
|
||||
if (img) {
|
||||
img.style.maxHeight = `${baseImgHeight * scaleFactor}px`;
|
||||
}
|
||||
|
||||
// Scale separator line margin
|
||||
const baseSeparatorMargin = 6;
|
||||
const separators = container.querySelectorAll('div[style*="border-top"]');
|
||||
separators.forEach(sep => {
|
||||
sep.style.marginTop = `${baseSeparatorMargin * scaleFactor}px`;
|
||||
sep.style.marginBottom = `${(baseSeparatorMargin + 2) * scaleFactor}px`;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates applied signature positions based on current zoom level
|
||||
* @param {HTMLElement} signatureLayer - Signature layer container
|
||||
* @param {number} currentPageNum - Current page number
|
||||
*/
|
||||
updateAppliedSignaturePositions(signatureLayer, currentPageNum) {
|
||||
if (!signatureLayer || !this._allSignatures) return;
|
||||
|
||||
const appliedContainers = signatureLayer.querySelectorAll('.applied-signature');
|
||||
appliedContainers.forEach(container => {
|
||||
const signatureId = parseInt(container.getAttribute('data-signature-id'));
|
||||
const signature = this._allSignatures.find(s => s.id === signatureId);
|
||||
|
||||
if (signature) {
|
||||
// ✅ Position calculation (same as renderSignatureButtons)
|
||||
const xPx = signature.x * this.scale;
|
||||
const yPx = signature.y * this.scale;
|
||||
|
||||
container.style.left = `${xPx}px`;
|
||||
container.style.top = `${yPx}px`;
|
||||
|
||||
// ✅ FIXED: Apply comprehensive scaling using helper method
|
||||
this.scaleAppliedSignature(container, this.scale);
|
||||
|
||||
// Show/hide based on current page
|
||||
container.style.display = (signature.page === currentPageNum) ? '' : 'none';
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears all signature buttons from the canvas.
|
||||
* Also hides applied signatures that don't belong to current page.
|
||||
@@ -864,24 +956,25 @@ window.pdfViewer = {
|
||||
});
|
||||
this.signatureButtons = [];
|
||||
|
||||
// Hide/show applied signatures based on current page
|
||||
const signatureLayer = document.getElementById('pdf-signature-layer');
|
||||
if (signatureLayer) {
|
||||
const appliedContainers = signatureLayer.querySelectorAll('.applied-signature');
|
||||
appliedContainers.forEach(container => {
|
||||
const signatureId = parseInt(container.getAttribute('data-signature-id'));
|
||||
const signature = this._allSignatures?.find(s => s.id === signatureId);
|
||||
// ✅ FIXED: Update applied signatures (position + scaling)
|
||||
this.appliedSignatureElements.forEach(container => {
|
||||
const signatureId = parseInt(container.getAttribute('data-signature-id'));
|
||||
const signature = this._allSignatures?.find(s => s.id === signatureId);
|
||||
|
||||
if (signature) {
|
||||
// Update position
|
||||
const xPx = signature.x * this.scale;
|
||||
const yPx = signature.y * this.scale;
|
||||
container.style.left = `${xPx}px`;
|
||||
container.style.top = `${yPx}px`;
|
||||
|
||||
if (signature) {
|
||||
// Show only if on current page, hide otherwise
|
||||
if (signature.page === this.pageNum) {
|
||||
container.style.display = ''; // Show
|
||||
} else {
|
||||
container.style.display = 'none'; // Hide
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// Update scaling
|
||||
this.scaleAppliedSignature(container, this.scale);
|
||||
|
||||
// Show/hide based on current page
|
||||
container.style.display = (signature.page === this.pageNum) ? '' : 'none';
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -938,6 +1031,14 @@ window.pdfViewer = {
|
||||
const signatureContainer = document.createElement('div');
|
||||
signatureContainer.className = 'applied-signature';
|
||||
signatureContainer.setAttribute('data-signature-id', signatureId);
|
||||
|
||||
// ✅ FIXED: Store base values for scaling
|
||||
signatureContainer.setAttribute('data-base-width', '230');
|
||||
signatureContainer.setAttribute('data-base-padding', '12');
|
||||
signatureContainer.setAttribute('data-base-font-size', '9');
|
||||
signatureContainer.setAttribute('data-base-img-height', '70');
|
||||
signatureContainer.setAttribute('data-base-border-radius', '6');
|
||||
|
||||
signatureContainer.style.position = 'absolute';
|
||||
signatureContainer.style.left = left;
|
||||
signatureContainer.style.top = top;
|
||||
@@ -969,6 +1070,8 @@ window.pdfViewer = {
|
||||
|
||||
// Text information container
|
||||
const infoContainer = document.createElement('div');
|
||||
infoContainer.className = 'signature-info-text'; // ✅ Class ekle (querySelector için)
|
||||
infoContainer.setAttribute('data-base-font-size', '9'); // ✅ Base font size sakla
|
||||
infoContainer.style.fontSize = '9px';
|
||||
infoContainer.style.lineHeight = '1.4';
|
||||
infoContainer.style.color = '#495057';
|
||||
@@ -1001,6 +1104,12 @@ window.pdfViewer = {
|
||||
|
||||
// Add to signature layer
|
||||
signatureLayer.appendChild(signatureContainer);
|
||||
|
||||
// ✅ FIXED: Track applied signature element for zoom updates
|
||||
this.appliedSignatureElements.push(signatureContainer);
|
||||
|
||||
// ✅ FIXED: Apply initial scaling based on current zoom
|
||||
this.scaleAppliedSignature(signatureContainer, this.scale);
|
||||
|
||||
console.log(`Signature #${signatureId} applied successfully`);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user