Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<Version>11.0.0-rc.2.1</Version>
</PropertyGroup>

<PropertyGroup>
<PackageTags>Bootstrap Blazor WebAssembly wasm UI Components SignaturePad</PackageTags>
<Description>Bootstrap UI components extensions of Plyr</Description>
</PropertyGroup>

<ItemGroup>
<Content Remove="Locales\en.json" />
<Content Remove="Locales\zh.json" />
<EmbeddedResource Include="Locales\en.json" />
<EmbeddedResource Include="Locales\zh.json" />
<Content Remove="Locales\*.json" />
<EmbeddedResource Include="Locales\*.json" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ namespace BootstrapBlazor.Components;
/// </summary>
public partial class SignaturePad
{
private const string StylePath = "_content/BootstrapBlazor.SignaturePad/SignaturePad.css";

#if NET9_0_OR_GREATER
private string StyleUrl => Assets[StylePath];
#else
private static string StyleUrl => StylePath;
#endif

/// <summary>
/// 手写签名结果回调/SignaturePad result callback method
/// </summary>
Expand Down Expand Up @@ -219,7 +227,7 @@ protected override void OnParametersSet()
/// <summary>
/// <inheritdoc/>
/// </summary>
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, EnableAlertJS ? SignatureAlertText : null, BackgroundColor);
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, EnableAlertJS ? SignatureAlertText : null, BackgroundColor, StyleUrl);

/// <summary>
/// 签名完成回调方法
Expand Down
71 changes: 35 additions & 36 deletions src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import './lib/signature_pad.umd.js';
import './lib/signature_pad.umd.js';
import Data from '../BootstrapBlazor/modules/data.js'
import { addLink } from '../BootstrapBlazor/modules/utility.js'

export async function init(id, invoke, alertText, backgroundColor) {
await addLink("./_content/BootstrapBlazor.SignaturePad/SignaturePad.css");
export async function init(id, invoke, alertText, backgroundColor, styleUrl) {
await addLink(styleUrl)

//Code modify from https://github.com/szimek/signature_pad
var element = document.getElementById(id);
var clearButton = element.querySelector("[data-action=clear]");
var changeColorButton = element.querySelector("[data-action=change-color]");
Expand All @@ -17,35 +16,42 @@ export async function init(id, invoke, alertText, backgroundColor) {
var closeButton = element.querySelector("[data-action=close]");
var canvas = element.querySelector("canvas");
var signaturePad = new SignaturePad(canvas, {
// It's Necessary to use an opaque color when saving image as JPEG;
// this option can be omitted if only saving as PNG or SVG
backgroundColor: backgroundColor || 'rgb(255, 255, 255)'
});

// Adjust canvas coordinate space taking into account pixel ratio,
// to make it look crisp on mobile devices.
// This also causes canvas to be cleared.
var previousWidth = canvas.parentElement.offsetWidth;
var previousHeight = canvas.parentElement.offsetHeight;

function resizeCanvas() {
// When zoomed out to less than 100%, for some very strange reason,
// some browsers report devicePixelRatio as less than 1
// and only part of the canvas is cleared then.
var ratio = devicePixelRatio;
var width = canvas.parentElement.offsetWidth;
var height = canvas.parentElement.offsetHeight;
var data = signaturePad.toData();
var scaleX = previousWidth > 0 ? width / previousWidth : 1;
var scaleY = previousHeight > 0 ? height / previousHeight : 1;
Comment on lines +30 to +31

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Resizing collapses saved signatures

When the pad is hidden or collapsed until its parent reports a zero width or height, then expands, resizeCanvas scales saved points to zero on a collapsed axis or records zero as the previous dimension; on expansion, the fallback scale of 1 cannot restore the original coordinates, so the signature appears collapsed, misplaced, clipped, or undersized.

Ignore zero-dimension resize measurements and preserve the last nonzero dimensions and saved point coordinates until the pad has nonzero dimensions again.

Also at src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.js:32.

Prompt for AI agents
In `src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.js` at lines 30-31:

**Resizing collapses saved signatures**

When the pad is hidden or collapsed until its parent reports a zero width or height, then expands, `resizeCanvas` scales saved points to zero on a collapsed axis or records zero as the previous dimension; on expansion, the fallback scale of 1 cannot restore the original coordinates, so the signature appears collapsed, misplaced, clipped, or undersized.

Ignore zero-dimension resize measurements and preserve the last nonzero dimensions and saved point coordinates until the pad has nonzero dimensions again.

Also at `src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.js:32`.


// This part causes the canvas to be cleared
canvas.width = canvas.parentElement.offsetWidth * ratio;
canvas.height = canvas.parentElement.offsetHeight * ratio;
canvas.width = width * ratio;
canvas.height = height * ratio;
canvas.getContext("2d").scale(ratio, ratio);

// This library does not listen for canvas changes, so after the canvas is automatically
// cleared by the browser, SignaturePad#isEmpty might still return false, even though the
// canvas looks empty, because the internal data of this library wasn't cleared. To make sure
// that the state of this library is consistent with visual state of the canvas, you
// have to clear it manually.
signaturePad.clear();
previousWidth = width;
previousHeight = height;

if (data.length > 0) {
signaturePad.fromData(data.map(group => ({
...group,
points: group.points.map(point => ({
...point,
x: point.x * scaleX,
y: point.y * scaleY
}))
})));
}
Comment on lines +41 to +49

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High · Resized signatures cannot be saved

When a signature is present and the canvas is resized, signaturePad.fromData() clears the pad before redrawing and leaves its isEmpty() flag set, so the save handlers treat the visible signature as empty and refuse to export it.

Restore the non-empty state after redrawing the signature, or use a redraw path that preserves the pad’s isEmpty() state.

Prompt for AI agents
In `src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.js` at lines 41-49:

**Resized signatures cannot be saved**

When a signature is present and the canvas is resized, `signaturePad.fromData()` clears the pad before redrawing and leaves its `isEmpty()` flag set, so the save handlers treat the visible signature as empty and refuse to export it.

Restore the non-empty state after redrawing the signature, or use a redraw path that preserves the pad’s `isEmpty()` state.

else {
signaturePad.clear();
}
}

// On mobile devices it might make more sense to listen to orientation change,
// rather than window resize events.
const observer = new ResizeObserver(resizeCanvas);
observer.observe(element);

Expand All @@ -65,14 +71,14 @@ export async function init(id, invoke, alertText, backgroundColor) {
document.body.appendChild(a);
a.click();

window.URL.revokeObjectURL(url);
setTimeout(() => {
window.URL.revokeObjectURL(url);
a.remove();
}, 1000);
}
}

// One could simply use Canvas#toBlob method instead, but it's just to show
// that it can be done using result of SignaturePad#toDataURL.
function dataURLToBlob(dataURL) {
// Code taken from https://github.com/ebidel/filer.js
var parts = dataURL.split(';base64,');
var contentType = parts[0].split(":")[1];
var raw = window.atob(parts[1]);
Expand All @@ -88,7 +94,6 @@ export async function init(id, invoke, alertText, backgroundColor) {

if (clearButton) clearButton.addEventListener("click", function (event) {
signaturePad.clear();
//return wrapperc.invokeMethodAsync("SignatureResult", null);
});

if (undoButton) undoButton.addEventListener("click", function (event) {
Expand All @@ -114,7 +119,6 @@ export async function init(id, invoke, alertText, backgroundColor) {
alertMessage();
} else {
var imgBase64 = signaturePad.toDataURL();
//console.log(imgBase64);
return invoke.invokeMethodAsync("SignatureResult", imgBase64);
}
});
Expand Down Expand Up @@ -158,12 +162,7 @@ export async function init(id, invoke, alertText, backgroundColor) {
}

export function dispose(id) {
const element = document.getElementById(id);
if (element) {
const observer = Data.get(id);

observer.unobserve(element);
observer.disconnect();
}
const observer = Data.get(id)?.observer;
observer?.disconnect();
Data.remove(id);
}
Loading