From 870ffb9711e38931b0b7b642d183ec00793b8893 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sun, 18 Oct 2026 14:29:56 +0800 Subject: [PATCH 1/5] fix(SignaturePad): fingerprint CSS asset Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../BootstrapBlazor.SignaturePad/SignaturePad.razor.cs | 10 +++++++++- .../BootstrapBlazor.SignaturePad/SignaturePad.razor.js | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.cs b/src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.cs index c25ab917..abf12826 100644 --- a/src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.cs +++ b/src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.cs @@ -13,6 +13,14 @@ namespace BootstrapBlazor.Components; /// 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 + /// /// 手写签名结果回调/SignaturePad result callback method /// @@ -219,7 +227,7 @@ protected override void OnParametersSet() /// /// /// - 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); /// /// 签名完成回调方法 diff --git a/src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.js b/src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.js index 832764cb..aefca76e 100644 --- a/src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.js +++ b/src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.js @@ -2,8 +2,8 @@ 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); From ae78393d0d86a3b3aef11ac5ab7acf3c75a49387 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 24 Oct 2026 09:10:49 +0800 Subject: [PATCH 2/5] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E5=B5=8C?= =?UTF-8?q?=E5=85=A5=E8=B5=84=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BootstrapBlazor.SignaturePad.csproj | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/BootstrapBlazor.SignaturePad/BootstrapBlazor.SignaturePad.csproj b/src/components/BootstrapBlazor.SignaturePad/BootstrapBlazor.SignaturePad.csproj index 0e8399c5..9cb276d2 100644 --- a/src/components/BootstrapBlazor.SignaturePad/BootstrapBlazor.SignaturePad.csproj +++ b/src/components/BootstrapBlazor.SignaturePad/BootstrapBlazor.SignaturePad.csproj @@ -6,10 +6,8 @@ - - - - + + From bf52b0dd5080829a78dc1f964f77b5fc5c2b486e Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 24 Oct 2026 09:11:06 +0800 Subject: [PATCH 3/5] chore: bump version 11.0.0-rc.2.1 --- .../BootstrapBlazor.SignaturePad.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/BootstrapBlazor.SignaturePad/BootstrapBlazor.SignaturePad.csproj b/src/components/BootstrapBlazor.SignaturePad/BootstrapBlazor.SignaturePad.csproj index 9cb276d2..f36e12bc 100644 --- a/src/components/BootstrapBlazor.SignaturePad/BootstrapBlazor.SignaturePad.csproj +++ b/src/components/BootstrapBlazor.SignaturePad/BootstrapBlazor.SignaturePad.csproj @@ -1,5 +1,9 @@ + + 11.0.0-rc.2.1 + + Bootstrap Blazor WebAssembly wasm UI Components SignaturePad Bootstrap UI components extensions of Plyr From f2b8235ad410c1936ba1033f48bf725526cb0076 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 24 Oct 2026 09:12:15 +0800 Subject: [PATCH 4/5] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E8=84=9A?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SignaturePad.razor.js | 67 +++++++++---------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.js b/src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.js index aefca76e..a4655b8e 100644 --- a/src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.js +++ b/src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.js @@ -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, 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]"); @@ -17,35 +16,42 @@ export async function init(id, invoke, alertText, backgroundColor, styleUrl) { 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; - // 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 + })) + }))); + } + 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); @@ -65,14 +71,14 @@ export async function init(id, invoke, alertText, backgroundColor, styleUrl) { 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]); @@ -88,7 +94,6 @@ export async function init(id, invoke, alertText, backgroundColor, styleUrl) { if (clearButton) clearButton.addEventListener("click", function (event) { signaturePad.clear(); - //return wrapperc.invokeMethodAsync("SignatureResult", null); }); if (undoButton) undoButton.addEventListener("click", function (event) { @@ -114,7 +119,6 @@ export async function init(id, invoke, alertText, backgroundColor, styleUrl) { alertMessage(); } else { var imgBase64 = signaturePad.toDataURL(); - //console.log(imgBase64); return invoke.invokeMethodAsync("SignatureResult", imgBase64); } }); @@ -158,12 +162,7 @@ export async function init(id, invoke, alertText, backgroundColor, styleUrl) { } 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); } From e17e7db773e65b19de1b894cf4c9fac7859860e0 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 24 Oct 2026 09:17:04 +0800 Subject: [PATCH 5/5] =?UTF-8?q?doc:=20=E6=9B=B4=E6=96=B0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SignaturePad.razor.cs | 96 ++++++++++++------- 1 file changed, 61 insertions(+), 35 deletions(-) diff --git a/src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.cs b/src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.cs index abf12826..8cf8ec05 100644 --- a/src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.cs +++ b/src/components/BootstrapBlazor.SignaturePad/SignaturePad.razor.cs @@ -1,15 +1,15 @@ -// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Website: https://www.blazor.zone or https://argozhang.github.io/ using Microsoft.AspNetCore.Components; using Microsoft.Extensions.Localization; -using Microsoft.JSInterop; namespace BootstrapBlazor.Components; /// -/// SignaturePad 签名 +/// 手写签名组件 +/// Signature pad component /// public partial class SignaturePad { @@ -22,160 +22,184 @@ public partial class SignaturePad #endif /// - /// 手写签名结果回调/SignaturePad result callback method + /// 手写签名结果回调 + /// Callback invoked with the signature result /// [Parameter] public Func? OnResult { get; set; } /// - /// 手写签名警告信息回调/SignaturePad alert callback method + /// 手写签名警告信息回调 + /// Callback invoked when a signature alert is raised /// [Parameter] public Func? OnAlert { get; set; } /// - /// 手写签名关闭信息回调/SignaturePad close callback method + /// 手写签名关闭回调 + /// Callback invoked when the signature pad is closed /// [Parameter] public Func? OnClose { get; set; } /// - /// 获得/设置 错误回调方法 + /// 获得或设置错误回调 + /// Gets or sets the error callback /// [Parameter] public Func? OnError { get; set; } /// - /// 在框内签名标签文本/Sign above label + /// 获得或设置签名区域上方的提示文本 + /// Gets or sets the label displayed above the signing area /// [Parameter] [NotNull] public string? SignAboveLabel { get; set; } /// - /// 清除按钮文本/Clear button title + /// 获得或设置清除按钮文本 + /// Gets or sets the clear button label /// [Parameter] [NotNull] public string? ClearBtnTitle { get; set; } /// - /// 请先签名提示文本/'Please provide a signature first' alert text + /// 获得或设置尚未签名时显示的提示文本 + /// Gets or sets the alert text shown when no signature is present /// [Parameter] [NotNull] public string? SignatureAlertText { get; set; } /// - /// 换颜色按钮文本/Change color button title + /// 获得或设置更改颜色按钮文本 + /// Gets or sets the change color button label /// [Parameter] [NotNull] public string? ChangeColorBtnTitle { get; set; } /// - /// 撤消按钮文本/Undo button title + /// 获得或设置撤销按钮文本 + /// Gets or sets the undo button label /// [Parameter] [NotNull] public string? UndoBtnTitle { get; set; } /// - /// 关闭按钮文本/Close button title + /// 获得或设置关闭按钮文本 + /// Gets or sets the close button label /// [Parameter] [NotNull] public string? CloseBtnTitle { get; set; } /// - /// 保存为 base64 按钮文本 Save as Base64 button title + /// 获得或设置保存为 Base64 按钮文本 + /// Gets or sets the save as Base64 button label /// [Parameter] [NotNull] public string? SaveBase64BtnTitle { get; set; } /// - /// 保存为PNG按钮文本/Save as PNG button title + /// 获得或设置保存为 PNG 按钮文本 + /// Gets or sets the save as PNG button label /// [Parameter] [NotNull] public string? SavePNGBtnTitle { get; set; } /// - /// 保存为JPG按钮文本/Save as JPG button title + /// 获得或设置保存为 JPG 按钮文本 + /// Gets or sets the save as JPG button label /// [Parameter] [NotNull] public string? SaveJPGBtnTitle { get; set; } /// - /// 保存为SVG按钮文本/Save as SVG button title + /// 获得或设置保存为 SVG 按钮文本 + /// Gets or sets the save as SVG button label /// [Parameter] [NotNull] public string? SaveSVGBtnTitle { get; set; } /// - /// 启用换颜色按钮/Enable change color button + /// 获得或设置是否启用更改颜色按钮 + /// Gets or sets whether the change color button is enabled /// [Parameter] [NotNull] public bool EnableChangeColorBtn { get; set; } = true; /// - /// 启用撤消按钮/Enable undo button + /// 获得或设置是否启用撤销按钮 + /// Gets or sets whether the undo button is enabled /// [Parameter] [NotNull] public bool EnableUndoBtn { get; set; } = true; /// - /// 启用JS错误弹窗/Enable Alert from JS + /// 获得或设置是否启用 JavaScript 提示框 + /// Gets or sets whether JavaScript alerts are enabled /// [Parameter] [NotNull] public bool EnableAlertJS { get; set; } = true; /// - /// 启用保存为 base64 按钮 Enable save as Base64 button + /// 获得或设置是否启用保存为 Base64 按钮 + /// Gets or sets whether the save as Base64 button is enabled /// [Parameter] [NotNull] public bool EnableSaveBase64Btn { get; set; } = true; /// - /// 启用保存为PNG按钮文本/Enable save as PNG button + /// 获得或设置是否启用保存为 PNG 按钮 + /// Gets or sets whether the save as PNG button is enabled /// [Parameter] [NotNull] public bool EnableSavePNGBtn { get; set; } /// - /// 启用保存为JPG按钮文本/Enable save as JPG button + /// 获得或设置是否启用保存为 JPG 按钮 + /// Gets or sets whether the save as JPG button is enabled /// [Parameter] [NotNull] public bool EnableSaveJPGBtn { get; set; } /// - /// 启用保存为SVG按钮文本/Enable save as SVG button + /// 获得或设置是否启用保存为 SVG 按钮 + /// Gets or sets whether the save as SVG button is enabled /// [Parameter] public bool EnableSaveSVGBtn { get; set; } /// - /// 按钮CSS式样/Button css style + /// 获得或设置按钮 CSS 类 + /// Gets or sets the CSS classes for buttons /// [Parameter] public string BtnCssClass { get; set; } = "btn btn-light"; /// - /// 按钮 "保存 "的css样式/Button "Save" css style + /// 获得或设置保存按钮的 CSS 类 + /// Gets or sets the CSS classes for save buttons /// [Parameter] public string BtnSaveCssClass { get; set; } = "btn btn-light"; /// - /// 组件CSS式样/Components css style + /// 获得或设置组件 CSS 类(已过期,请直接使用 class) + /// Gets or sets the component CSS class (obsolete; use class instead) /// [Parameter] [NotNull] @@ -183,14 +207,16 @@ public partial class SignaturePad public string CssClass { get; set; } = "signature-pad-body"; /// - /// 响应式css界面,为所有用户设计最佳体验/Enable Responsive css + /// 获得或设置是否启用响应式布局 + /// Gets or sets whether responsive layout is enabled /// [Parameter] [NotNull] public bool Responsive { get; set; } /// - /// 组件背景 backgroundColor + /// 获得或设置签名画布的背景颜色 + /// Gets or sets the signature canvas background color /// [Parameter] public string? BackgroundColor { get; set; } @@ -230,10 +256,10 @@ protected override void OnParametersSet() protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop, EnableAlertJS ? SignatureAlertText : null, BackgroundColor, StyleUrl); /// - /// 签名完成回调方法 + /// 调用签名结果回调 + /// Invokes the signature result callback /// /// - /// [JSInvokable] public async Task SignatureResult(string? val) { @@ -241,9 +267,9 @@ public async Task SignatureResult(string? val) } /// - /// 告警回调方法 + /// 调用签名提示回调 + /// Invokes the signature alert callback /// - /// [JSInvokable] public async Task SignatureAlert() { @@ -254,9 +280,9 @@ public async Task SignatureAlert() } /// - /// 关闭回调方法 + /// 调用签名板关闭回调 + /// Invokes the signature pad close callback /// - /// [JSInvokable] public async Task Close() {