Improve drag-and-drop in SenderPdfImageEditor
Replaced the old drag-and-drop implementation with a custom solution using mouse events for better control and precision. Added `_isDragging` state and dynamic cursor updates to enhance user feedback during drag operations. Introduced grab offset calculations to ensure accurate field positioning relative to the pointer. Added `OnEditorMouseUp` to reset the drag state and `GetPageSize` to retrieve page dimensions, defaulting to A4 if unavailable. Updated `envelope-editor.js` to include `pageNumber` in `getClickCoordsOnPdfPage` and removed the obsolete `getDropCoordsOnPage` function. Enhanced `EditorCoords` to include `PageNumber` for improved page identification.
This commit is contained in:
@@ -5,8 +5,11 @@
|
|||||||
@inject IJSRuntime JSRuntime
|
@inject IJSRuntime JSRuntime
|
||||||
|
|
||||||
<div class="pdf-editor-wrapper sender-image-editor sender-editor-pdf-viewer"
|
<div class="pdf-editor-wrapper sender-image-editor sender-editor-pdf-viewer"
|
||||||
style="cursor: @(IsPlacementMode ? "crosshair" : "default");"
|
style="cursor: @(IsPlacementMode ? "crosshair" : (_isDragging ? "grabbing" : "default"));"
|
||||||
@onclick="HandleClickAsync">
|
@onclick="HandleClickAsync"
|
||||||
|
@onmousemove="OnEditorMouseMoveAsync"
|
||||||
|
@onmouseup="OnEditorMouseUp"
|
||||||
|
@onmouseleave="OnEditorMouseUp">
|
||||||
@if (PageCount <= 0)
|
@if (PageCount <= 0)
|
||||||
{
|
{
|
||||||
<div class="sender-image-editor__empty">@L("SenderEditor_NoPagesToDisplay", "Keine Seiten zur Anzeige verfügbar.")</div>
|
<div class="sender-image-editor__empty">@L("SenderEditor_NoPagesToDisplay", "Keine Seiten zur Anzeige verfügbar.")</div>
|
||||||
@@ -36,11 +39,7 @@
|
|||||||
data-page-height-pt="@heightPt">
|
data-page-height-pt="@heightPt">
|
||||||
<div class="sender-image-editor__page-canvas"
|
<div class="sender-image-editor__page-canvas"
|
||||||
data-editor-page-canvas="@pageNumber"
|
data-editor-page-canvas="@pageNumber"
|
||||||
ondragover="event.preventDefault(); if (event.dataTransfer) event.dataTransfer.dropEffect = 'move';"
|
style="@( _isDragging ? "cursor:grabbing;" : string.Empty)">
|
||||||
ondragenter="event.preventDefault();"
|
|
||||||
@ondrop:preventDefault="true"
|
|
||||||
@ondrop:stopPropagation="true"
|
|
||||||
@ondrop="args => OnPageDropAsync(args, pageNumber, widthPt, heightPt)">
|
|
||||||
@if (!string.IsNullOrWhiteSpace(pagePreview?.ImageDataUrl))
|
@if (!string.IsNullOrWhiteSpace(pagePreview?.ImageDataUrl))
|
||||||
{
|
{
|
||||||
<img class="sender-image-editor__page-image"
|
<img class="sender-image-editor__page-image"
|
||||||
@@ -58,9 +57,8 @@
|
|||||||
<div class="sender-image-editor__field"
|
<div class="sender-image-editor__field"
|
||||||
style="@GetFieldStyle(field, widthPt, heightPt)"
|
style="@GetFieldStyle(field, widthPt, heightPt)"
|
||||||
title="@field.ReceiverName"
|
title="@field.ReceiverName"
|
||||||
draggable="true"
|
@onmousedown="args => OnFieldMouseDownAsync(args, field)"
|
||||||
@onmousedown="() => OnFieldDragStart(field.Index)"
|
@onmousedown:preventDefault="true"
|
||||||
ondragstart="event.dataTransfer.setData('text/plain', 'field'); event.dataTransfer.effectAllowed = 'move';"
|
|
||||||
@onclick:stopPropagation="true">
|
@onclick:stopPropagation="true">
|
||||||
<span class="sender-image-editor__field-label">@field.ReceiverName</span>
|
<span class="sender-image-editor__field-label">@field.ReceiverName</span>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
@@ -108,6 +106,9 @@
|
|||||||
public EventCallback<SenderPdfFieldMoveRequest> MoveField { get; set; }
|
public EventCallback<SenderPdfFieldMoveRequest> MoveField { get; set; }
|
||||||
|
|
||||||
private int? _draggedFieldIndex;
|
private int? _draggedFieldIndex;
|
||||||
|
private bool _isDragging;
|
||||||
|
private double _grabOffsetXPt;
|
||||||
|
private double _grabOffsetYPt;
|
||||||
|
|
||||||
private static string GetFieldStyle(SenderPdfFieldOverlay field, double pageWidthPt, double pageHeightPt)
|
private static string GetFieldStyle(SenderPdfFieldOverlay field, double pageWidthPt, double pageHeightPt)
|
||||||
{
|
{
|
||||||
@@ -131,39 +132,69 @@
|
|||||||
await EditorClick.InvokeAsync(args);
|
await EditorClick.InvokeAsync(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnFieldDragStart(int fieldIndex)
|
private async Task OnFieldMouseDownAsync(MouseEventArgs args, SenderPdfFieldOverlay field)
|
||||||
{
|
{
|
||||||
_draggedFieldIndex = fieldIndex;
|
var coords = await JSRuntime.InvokeAsync<EditorCoords?>(
|
||||||
}
|
"envelopeEditor.getClickCoordsOnPdfPage",
|
||||||
|
|
||||||
private async Task OnPageDropAsync(DragEventArgs args, int pageNumber, double pageWidthPt, double pageHeightPt)
|
|
||||||
{
|
|
||||||
if (_draggedFieldIndex is null || !MoveField.HasDelegate)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var coords = await JSRuntime.InvokeAsync<DropCoords?>(
|
|
||||||
"envelopeEditor.getDropCoordsOnPage",
|
|
||||||
"sender-editor-pdf-viewer",
|
"sender-editor-pdf-viewer",
|
||||||
pageNumber,
|
|
||||||
args.ClientX,
|
args.ClientX,
|
||||||
args.ClientY);
|
args.ClientY);
|
||||||
|
|
||||||
if (coords is null)
|
if (coords is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var pageNumber = coords.PageNumber;
|
||||||
|
var (pageWidthPt, pageHeightPt) = GetPageSize(pageNumber);
|
||||||
|
var pointerXPt = coords.NormX * pageWidthPt;
|
||||||
|
var pointerYPt = coords.NormY * pageHeightPt;
|
||||||
|
|
||||||
|
_draggedFieldIndex = field.Index;
|
||||||
|
_isDragging = true;
|
||||||
|
_grabOffsetXPt = pointerXPt - field.XPt;
|
||||||
|
_grabOffsetYPt = pointerYPt - field.YPt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnEditorMouseMoveAsync(MouseEventArgs args)
|
||||||
|
{
|
||||||
|
if (_draggedFieldIndex is null || !MoveField.HasDelegate)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var coords = await JSRuntime.InvokeAsync<EditorCoords?>(
|
||||||
|
"envelopeEditor.getClickCoordsOnPdfPage",
|
||||||
|
"sender-editor-pdf-viewer",
|
||||||
|
args.ClientX,
|
||||||
|
args.ClientY);
|
||||||
|
|
||||||
|
if (coords is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var pageNumber = coords.PageNumber;
|
||||||
|
var (pageWidthPt, pageHeightPt) = GetPageSize(pageNumber);
|
||||||
|
|
||||||
const double sigWidthPt = 1.77 * 72;
|
const double sigWidthPt = 1.77 * 72;
|
||||||
const double sigHeightPt = 1.96 * 72;
|
const double sigHeightPt = 1.96 * 72;
|
||||||
|
|
||||||
var xPt = coords.NormX * pageWidthPt;
|
var xPt = (coords.NormX * pageWidthPt) - _grabOffsetXPt;
|
||||||
var yPt = coords.NormY * pageHeightPt;
|
var yPt = (coords.NormY * pageHeightPt) - _grabOffsetYPt;
|
||||||
xPt = Math.Max(0, Math.Min(xPt, pageWidthPt - sigWidthPt));
|
xPt = Math.Max(0, Math.Min(xPt, pageWidthPt - sigWidthPt));
|
||||||
yPt = Math.Max(0, Math.Min(yPt, pageHeightPt - sigHeightPt));
|
yPt = Math.Max(0, Math.Min(yPt, pageHeightPt - sigHeightPt));
|
||||||
|
|
||||||
await MoveField.InvokeAsync(new SenderPdfFieldMoveRequest(_draggedFieldIndex.Value, pageNumber, xPt, yPt));
|
await MoveField.InvokeAsync(new SenderPdfFieldMoveRequest(_draggedFieldIndex.Value, pageNumber, xPt, yPt));
|
||||||
_draggedFieldIndex = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed record DropCoords(double NormX, double NormY);
|
private void OnEditorMouseUp(MouseEventArgs _)
|
||||||
|
{
|
||||||
|
_draggedFieldIndex = null;
|
||||||
|
_isDragging = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private (double WidthPt, double HeightPt) GetPageSize(int pageNumber)
|
||||||
|
{
|
||||||
|
var page = Pages.FirstOrDefault(p => p.PageNumber == pageNumber);
|
||||||
|
return page is null ? (595.0, 842.0) : (page.WidthPt, page.HeightPt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed record EditorCoords(double NormX, double NormY, int PageIndex, int PageNumber);
|
||||||
|
|
||||||
private string L(string key, string fallback)
|
private string L(string key, string fallback)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -90,33 +90,8 @@ window.envelopeEditor = {
|
|||||||
return {
|
return {
|
||||||
normX: normX,
|
normX: normX,
|
||||||
normY: normY,
|
normY: normY,
|
||||||
pageIndex: targetIndex
|
pageIndex: targetIndex,
|
||||||
};
|
pageNumber: parseInt(targetPage.getAttribute('data-editor-page') || String(targetIndex + 1), 10)
|
||||||
},
|
|
||||||
|
|
||||||
getDropCoordsOnPage: function (viewerCssClass, pageNumber, clientX, clientY) {
|
|
||||||
const viewer = document.querySelector('.' + viewerCssClass);
|
|
||||||
if (!viewer) {
|
|
||||||
console.warn('[envelopeEditor] viewer not found for class:', viewerCssClass);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const page = viewer.querySelector('[data-editor-page="' + pageNumber + '"]');
|
|
||||||
if (!page) {
|
|
||||||
console.warn('[envelopeEditor] page not found:', pageNumber);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const rect = page.getBoundingClientRect();
|
|
||||||
const clampedX = Math.max(rect.left, Math.min(clientX, rect.right));
|
|
||||||
const clampedY = Math.max(rect.top, Math.min(clientY, rect.bottom));
|
|
||||||
|
|
||||||
const normX = (clampedX - rect.left) / rect.width;
|
|
||||||
const normY = (clampedY - rect.top) / rect.height;
|
|
||||||
|
|
||||||
return {
|
|
||||||
normX: normX,
|
|
||||||
normY: normY
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user