diff --git a/Demo.Wasm/Pages/BarcodeReaderPage.razor b/Demo.Wasm/Pages/BarcodeReaderPage.razor deleted file mode 100644 index 90ae217..0000000 --- a/Demo.Wasm/Pages/BarcodeReaderPage.razor +++ /dev/null @@ -1,347 +0,0 @@ -@page "/BarcodeReader" - -

条码扫描 BarcodeReader

- -

条码,QR码,Pdf417

- - -
- -只解码 Pdf417 格式 -
- - -连续解码 -
- - -解码所有编码形式 (+ PDF_417 / DATA_MATRIX...) -
- -@if (Pdf417 | DecodeAllFormats) -{ - Result: -
-
@BarCode
-} -else if (DecodeContinuously) -{ - DecodeContinuously: -
-
@BarCode
-} -else -{ - -} - -@if (ShowScanBarcode) -{ - - -} -
- - - -@if (ShowCodes) -{ -
-    
-        @@if (ShowScanBarcode)
-        {
-
-            <BarcodeReader ScanResult="((e) => { BarCode=e; ShowScanBarcode = !ShowScanBarcode; })"
-                           Close="(()=>ShowScanBarcode=!ShowScanBarcode)"
-                           OnError="OnError" />
-            @@code{
-                bool ShowScanBarcode { get; set; } = false;
-
-                public string? BarCode { get; set; }
-
-                private string message;
-
-                private Task OnError(string message)
-                {
-                    this.message = message;
-                    StateHasChanged();
-                    return Task.CompletedTask;
-                }
-
-            }
-        }
-    
-
-} -
- - -
- -@if (ShowScanBarcodeCustom) -{ - - -
-
- - - - -
- - -
-
- -
-
-
- - -} -

@message

- -
-
-
-
-
- -

事件 Events

- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ScanResult
扫码结果回调方法/Scan result callback method
EventCallback<string>
Close
关闭扫码框回调方法/Close scan code callback method
EventCallback
ScanBtnTitle
扫码按钮文本/Scan button title
Parameter
ResetBtnTitle
复位按钮文本/Reset button title
Parameter
CloseBtnTitle
关闭按钮文本/Close button title
Parameter
Pdf417
只解码 Pdf417 格式
Parameter
Decodeonce
单次|连续解码,默认单次
Parameter
DecodeAllFormats
解码所有编码形式,性能较差, 开启后可用 options.formats 指定编码形式.默认为 false
Parameter
options
ZXingOptions 选项: Pdf417 | Decodeonce | TimeBetweenDecodingAttempts | DecodeAllFormats | formats
Parameter
SelectDeviceBtnTitle
选择设备按钮文本
Parameter
OnError
错误信息回调
Func<string, Task>
UseBuiltinDiv
使用内置扫码界面,默认True
bool
参数/Parameter
说明/Description
类型/Type
-
- -
-
- - -
- -
- -

条码格式 BarcodeFormat

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1D product1D industrial2D
UPC-ACode 39QR Code
UPC-ECode 93Data Matrix
EAN-8Code 128Aztec
EAN-13CodabarPDF 417
ITFMaxiCode
RSS-14
RSS-Expanded *
-
- -
@ver
-
@build
- -@code{ - - /// - /// 显示扫码界面 - /// - bool ShowScanBarcode { get; set; } = false; - - /// - /// 条码 - /// - public string? BarCode { get; set; } - - public bool Pdf417 { get; set; } - public bool DecodeContinuously { get; set; } - public bool DecodeAllFormats { get; set; } - - private string message; - - private Task OnError(string message) - { - this.message = message; - StateHasChanged(); - return Task.CompletedTask; - } - - bool ShowCodes; - - private void ScanResult(string e) - { - if (!DecodeContinuously) - { - BarCode = e; - ShowScanBarcode = !ShowScanBarcode; - } - else - { - if (BarCode.Length > 60) BarCode = ""; - BarCode += e + Environment.NewLine; - } - } - - #region Custom - - /// - /// 显示扫码界面Custom - /// - bool ShowScanBarcodeCustom { get; set; } = false; - - /// - /// 条码 - /// - public string? BarCodeCustom { get; set; } - - BarcodeReader barcodeReaderCustom; - - #endregion - - - string? ver { get; set; } - string? build { get; set; } - - - Version? version { get; set; } = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; - - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - - ver = $"Ver {version?.Major}.{version?.Minor}.{version?.Build}"; - build = "Build:" + File.GetLastWriteTime(Path.Combine(Directory.GetCurrentDirectory(), "Demo.Server.dll")).ToLocalTime().ToString("yyyy-MM-dd HH:mm"); - - } -} diff --git a/Demo.Wasm/Pages/HandwrittenPage.razor b/Demo.Wasm/Pages/HandwrittenPage.razor deleted file mode 100644 index e39c1e6..0000000 --- a/Demo.Wasm/Pages/HandwrittenPage.razor +++ /dev/null @@ -1,109 +0,0 @@ -@page "/handwritten" - -

Handwritten 手写签名

- - - + + @if (!string.IsNullOrWhiteSpace(CapturedImageDataUrl)) { +
+ @L( +
@L("最近一次扫码截图", "Latest captured frame")
+
+ } + + - <BarcodeReader ScanResult="((e) => { BarCode=e; ShowScanBarcode = !ShowScanBarcode; })" - Close="(()=>ShowScanBarcode=!ShowScanBarcode)" - OnError="OnError" /> - @@code{ - bool ShowScanBarcode { get; set; } = false; - - public string? BarCode { get; set; } - - private string message; - - private Task OnError(string message) - { - this.message = message; - StateHasChanged(); - return Task.CompletedTask; - } +
+
+
+

@L("从图片解码", "Decode from an image")

+

@L("选择本地图片并识别其中的条码。", "Choose a local image and decode its barcode.")

+
+ +
+ +
+ +
+
+
+

@L("自定义扫码界面", "Custom scanner UI")

+

@L("关闭内置界面后,可用 data-action 元素控制扫码器。", "Disable the built-in UI and control the scanner with data-action elements.")

+
+ +
- } - } - - -} -

@message

-
- - - -
- - -
- -@if (ShowScanBarcodeCustom) -{ - - -
-
-
- -
+ @if (ShowCustomScanner) + { + + +
+
- - + + +
+
+ + + +
- - - - +
+ } +
+
+
+
+

@L("使用示例", "Usage")

+

@L("截图通过流式 JS 互操作返回,适用于 Blazor WebAssembly 和 Blazor Server。", "Captured frames are returned through streaming JS interop and work with both Blazor WebAssembly and Blazor Server.")

+
- - -} -
-
-
-
-
- -

Events

- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @if (ShowCode) + { +
@CodeSample
+ } + + +
+

@L("事件", "Events")

+

@L("组件向调用方报告扫码结果、截图、关闭操作和错误。", "The component reports scan results, captured frames, close actions, and errors to its caller.")

+
+
ScanResult
Scan result callback method
EventCallback<string>
Close
Close scan code callback method
EventCallback
ScanBtnTitle
Scan button title
Parameter
ResetBtnTitle
Reset button title
Parameter
CloseBtnTitle
Close button title
Parameter
Pdf417
decode only Pdf417 format
Parameter
Decodeonce
Decode Once or Decode Continuously, default is Once
Parameter
DecodeAllFormats
Decodde All Formats, performance is poor, you can set options.formats to customize specify the encoding formats. The default is false
Parameter
options
-
- ZXingOptions: Pdf417 | Decodeonce | TimeBetweenDecodingAttempts | DecodeAllFormats | formats,
- more... -
-
Parameter
Parameter
SelectDeviceBtnTitle
Select device button title
Parameter
OnError
Error callback method
Func<string, Task>
+ + + + + + + + + @foreach (var item in Events) + { - - - + + + - - + } + +
@L("事件", "Event")@L("类型", "Type")@L("说明", "Description")
UseBuiltinDiv
Use builtin interface,defallt True
bool
@item.Name@item.Type@L(item.Zh, item.En)
+
+
+ +
+

@L("主要参数", "Key parameters")

+

+ @L("以下参数覆盖界面、解码模式、摄像头选择和最新的扫码截图能力。完整高级解码提示可在 ZXingOptions 中配置。", + "These parameters cover UI, decoding modes, camera selection, and the latest scan-capture feature. Configure advanced decoding hints through ZXingOptions.") +

+
+ + + + + + + + + + + @foreach (var item in Parameters) + { - - - + + + + - -
@L("参数", "Parameter")@L("类型", "Type")@L("默认值", "Default")@L("说明", "Description")
Parameter
Description
Type
@item.Name@item.Type@item.DefaultValue@L(item.Zh, item.En)
+ } + + +
+ + @L("查看完整 ZXingOptions", "View the complete ZXingOptions") + +
+ +
+

@L("支持的条码格式", "Supported barcode formats")

+
+
+

@L("商品条码", "Product")

+

UPC-A · UPC-E · EAN-8 · EAN-13

+
+
+

@L("工业条码", "Industrial")

+

Code 39 · Code 93 · Code 128 · Codabar · ITF · RSS-14 · RSS-Expanded

+
+
+

@L("二维条码", "2D")

+

QR Code · Data Matrix · Aztec · PDF417 · MaxiCode

-
- - -
- -
- -

BarcodeFormat

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1D product1D industrial2D
UPC-ACode 39QR Code
UPC-ECode 93Data Matrix
EAN-8Code 128Aztec
EAN-13CodabarPDF 417
ITFMaxiCode
RSS-14
RSS-Expanded *
-
- -
@ver
-
@build
- -@code{ - BarCodes barCodes; - - /// - /// Display the scan code interface - /// - bool ShowScanBarcode { get; set; } = false; - - /// - /// BarCode - /// - public string? BarCode { get; set; } - - public bool Pdf417 { get; set; } - public bool DecodeContinuously { get; set; } - public bool DecodeAllFormats { get; set; } - - private string message; - - private Task OnError(string message) +
+ +
ZXingBlazor @VersionText · .NET 10
+ +@code { + private readonly record struct ApiItem(string Name, string Type, string DefaultValue, string Zh, string En); + + private static readonly ApiItem[] Events = + [ + new("ScanResult", "EventCallback", "-", "返回解码后的文本。启用截图时,在 ScanCaptured 完成后触发。", "Returns decoded text. When capture is enabled, it fires after ScanCaptured completes."), + new("ScanCaptured", "EventCallback", "-", "返回解码文本和当前视频帧的 JPEG Data URL。", "Returns the decoded text and a JPEG data URL for the current video frame."), + new("Close", "EventCallback", "-", "用户关闭扫码界面时触发。", "Raised when the user closes the scanner UI."), + new("OnError", "Func", "-", "摄像头、解码器或 JS 互操作出错时返回错误消息。", "Reports camera, decoder, or JS interop errors.") + ]; + + private static readonly ApiItem[] Parameters = + [ + new("CaptureStillOnScan", "bool", "false", "扫码成功时冻结预览并捕获 JPEG 画面。", "Freezes the preview and captures a JPEG frame after a successful scan."), + new("CaptureStillAutoResumeDelay", "int", "3000", "捕获后恢复实时预览的延迟,单位为毫秒;最小值为 0。", "Delay in milliseconds before live preview resumes after capture; values are clamped to zero or greater."), + new("Decodeonce", "bool", "true", "单次解码;设为 false 时连续返回结果。", "Decodes once; set to false to keep reporting results."), + new("DecodeAllFormats", "bool", "false", "尝试全部格式,可能降低性能;可通过 Options.formats 缩小范围。", "Tries every format and may reduce performance; narrow the list with Options.formats."), + new("Pdf417Only", "bool", "false", "仅启用 PDF417 解码。", "Enables PDF417-only decoding."), + new("AlsoInverted", "bool", "true", "同时识别黑底白码等反色条码,并复用同一摄像头流。", "Also recognizes inverted barcodes, such as white-on-black codes, using the same camera stream."), + new("DeviceID", "string?", "null", "指定首选摄像头设备 ID。", "Selects a preferred camera device ID."), + new("SaveDeviceID", "bool", "true", "保存最后使用且仍然有效的摄像头设备 ID。", "Remembers the last selected camera when that device is still available."), + new("UseBuiltinDiv", "bool", "true", "使用组件内置扫码界面;设为 false 可提供自定义 data-action 元素。", "Uses the built-in scanner UI; set to false to provide custom data-action elements."), + new("Style", "ZXingBlazorStyle", "Modal", "选择内置界面的显示样式。", "Selects the presentation style of the built-in UI."), + new("Options", "ZXingOptions?", "null", "提供格式、图像尺寸、质量和高级 ZXing 解码提示。", "Provides formats, image dimensions, quality, and advanced ZXing decoding hints."), + new("ScanBtnTitle", "string", "\"扫码\"", "扫码按钮文本。", "Text shown on the scan button."), + new("ResetBtnTitle", "string", "\"复位\"", "重置按钮文本。", "Text shown on the reset button."), + new("CloseBtnTitle", "string", "\"关闭\"", "关闭按钮文本。", "Text shown on the close button."), + new("SelectDeviceBtnTitle", "string", "\"选择设备\"", "摄像头选择按钮文本。", "Text shown on the camera selection button.") + ]; + + private const string CodeSample = """ + + +@code { + private void OnScanResult(string text) { } + private void OnScanCaptured(ScanCapturedEventArgs args) { - this.message = message; - StateHasChanged(); - return Task.CompletedTask; + // args.Text and args.ImageDataUrl + } +} +"""; + + private BarCodes barCodes = default!; + private BarcodeReader barcodeReaderCustom = default!; + private readonly ZXingOptions options = new() { TRY_HARDER = true }; + private bool IsEnglish; + private bool ShowScanBarcode; + private bool ShowCustomScanner; + private bool ShowCode; + private bool Pdf417Only; + private bool DecodeContinuously; + private bool DecodeAllFormats; + private bool CaptureStillOnScan; + private int CaptureStillAutoResumeDelay = 3000; + private string BarCode = string.Empty; + private string? CapturedImageDataUrl; + private string? ErrorMessage; + + private string VersionText => + typeof(BarcodeReader).Assembly.GetName().Version?.ToString(3) ?? string.Empty; + + private string L(string zh, string en) => IsEnglish ? en : zh; + + protected override void OnInitialized() + { + IsEnglish = string.Equals( + NavigationManager.ToBaseRelativePath(NavigationManager.Uri).Trim('/'), + "en", + StringComparison.OrdinalIgnoreCase); } - bool ShowCodes; + private void ToggleLanguage() => IsEnglish = !IsEnglish; - private void ScanResult(string e) + private void ToggleScanner() { - if (!DecodeContinuously) - { - BarCode = e; - ShowScanBarcode = !ShowScanBarcode; - } - else + ShowScanBarcode = !ShowScanBarcode; + ErrorMessage = null; + if (ShowScanBarcode) { - if (BarCode.Length > 200) BarCode = ""; - BarCode += e + Environment.NewLine; + BarCode = string.Empty; + CapturedImageDataUrl = null; } } - #region Custom - - /// - /// Display the Custom scan code interface - /// - bool ShowScanBarcodeCustom { get; set; } = false; - - /// - /// BarCodeCustom - /// - public string? BarCodeCustom { get; set; } - - BarcodeReader barcodeReaderCustom; - - #endregion + private void CloseScanner() => ShowScanBarcode = false; + private void OnScanResult(string text) + { + if (DecodeContinuously) + { + if (BarCode.Length > 500) + { + BarCode = string.Empty; + } - string? ver { get; set; } - string? build { get; set; } - + BarCode += text + Environment.NewLine; + return; + } - Version? version { get; set; } = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; + BarCode = text; + ShowScanBarcode = false; + } - protected override async Task OnInitializedAsync() + private void OnScanCaptured(ScanCapturedEventArgs args) { - await base.OnInitializedAsync(); - - ver = $"Ver {version?.Major}.{version?.Minor}.{version?.Build}"; + BarCode = args.Text; + CapturedImageDataUrl = args.ImageDataUrl; + } + private Task OnError(string message) + { + ErrorMessage = message; + return Task.CompletedTask; } - private ZXingOptions options = new ZXingOptions() { TRY_HARDER = true }; + private void ToggleCustomScanner() => ShowCustomScanner = !ShowCustomScanner; - private Task OnResult(string message) + private void OnCustomScanResult(string text) { - this.BarCode = message; - StateHasChanged(); - return Task.CompletedTask; + BarCode = text; + ShowCustomScanner = false; } - private async Task DecodeFromImage() + private Task OnImageResult(string message) { - this.BarCode = ""; - this.message = ""; - await barCodes!.DecodeFromImage(); + BarCode = message; + return Task.CompletedTask; } + + private Task DecodeFromImage() => barCodes.DecodeFromImage(); } diff --git a/Demo.Wasm/Pages/Index.razor.css b/Demo.Wasm/Pages/Index.razor.css new file mode 100644 index 0000000..f80ac04 --- /dev/null +++ b/Demo.Wasm/Pages/Index.razor.css @@ -0,0 +1,192 @@ +.demo-header, +.section-heading { + align-items: flex-start; + display: flex; + gap: 1.5rem; + justify-content: space-between; +} + +.demo-header { + background: linear-gradient(135deg, #f4f8ff, #eefbf7); + border: 1px solid #dce8f5; + border-radius: 1rem; + margin-bottom: 1.5rem; + padding: clamp(1.25rem, 4vw, 2.5rem); +} + +.demo-header h1 { + font-size: clamp(2rem, 6vw, 3.5rem); + letter-spacing: -0.04em; + margin: 0.15rem 0 0.5rem; +} + +.demo-header p, +.section-heading p, +.demo-section > p { + color: #5a6572; + margin-bottom: 0; +} + +.eyebrow { + color: #0b6bcb; + font-size: 0.78rem; + font-weight: 700; + letter-spacing: 0.12em; + text-transform: uppercase; +} + +.language-button { + flex: 0 0 auto; +} + +.demo-section { + border: 1px solid #e1e7ed; + border-radius: 0.85rem; + margin-bottom: 1.25rem; + padding: clamp(1rem, 3vw, 1.75rem); +} + +.demo-section h2 { + font-size: 1.35rem; + margin-bottom: 0.35rem; +} + +.demo-section h3 { + font-size: 1rem; +} + +.section-heading { + margin-bottom: 1.25rem; +} + +.option-grid { + display: grid; + gap: 0.75rem; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + margin: 1rem 0; +} + +.form-check { + background: #f7f9fb; + border: 1px solid #e1e7ed; + border-radius: 0.6rem; + cursor: pointer; + margin: 0; + padding: 0.8rem 0.9rem 0.8rem 2.25rem; +} + +.delay-setting { + margin-bottom: 1rem; + max-width: 280px; +} + +.delay-setting label { + display: block; + font-weight: 600; + margin-bottom: 0.4rem; +} + +.result-grid { + display: grid; + gap: 1rem; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + margin-top: 1rem; +} + +.result-field { + font-family: SFMono-Regular, Consolas, "Liberation Mono", monospace; +} + +.capture-preview { + margin: 0; +} + +.capture-preview img { + border: 1px solid #d5dde5; + border-radius: 0.6rem; + display: block; + max-height: 320px; + object-fit: contain; + width: 100%; +} + +.capture-preview figcaption { + color: #5a6572; + font-size: 0.85rem; + margin-top: 0.45rem; +} + +.custom-scanner { + max-width: 640px; +} + +.custom-scanner video { + background: #101820; + border-radius: 0.7rem; + display: block; + max-height: 480px; + width: 100%; +} + +.custom-actions { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-top: 0.75rem; +} + +.code-block { + background: #101820; + border-radius: 0.65rem; + color: #eaf2f8; + margin: 0; + overflow-x: auto; + padding: 1rem; +} + +.api-table { + min-width: 720px; +} + +.api-table code { + color: #174f88; + white-space: nowrap; +} + +.format-groups { + display: grid; + gap: 1rem; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + margin-top: 1rem; +} + +.format-groups > div { + background: #f7f9fb; + border-radius: 0.6rem; + padding: 1rem; +} + +.format-groups p { + color: #5a6572; + margin: 0; +} + +.demo-footer { + color: #71808f; + font-size: 0.85rem; + padding: 0.5rem 0 1.5rem; + text-align: center; +} + +@media (max-width: 640px) { + .demo-header, + .section-heading { + align-items: stretch; + flex-direction: column; + } + + .language-button, + .section-heading .btn { + align-self: flex-start; + } +} diff --git a/Demo.Wasm/Pages/Viewer.razor b/Demo.Wasm/Pages/Viewer.razor deleted file mode 100644 index 8e5ee9e..0000000 --- a/Demo.Wasm/Pages/Viewer.razor +++ /dev/null @@ -1,25 +0,0 @@ -@page "/viewer" - @using ZXingBlazor.Components - - - - @code{ - List Images; - ViewerOptions ViewerOptions = new ViewerOptions() - { - toolbarlite = true, - }; - - protected override void OnInitialized() - { - - Images = new List(); - if (!Images.Any()) - { - for (int i = 1; i <= 9; i++) - { - Images.Add($"https://fengyuanchen.github.io/viewerjs/images/thumbnails/tibet-{i}.jpg"); - } - } - } - } diff --git a/Demo.Wasm/Shared/NavMenu.razor b/Demo.Wasm/Shared/NavMenu.razor index c07da6e..bbab1cc 100644 --- a/Demo.Wasm/Shared/NavMenu.razor +++ b/Demo.Wasm/Shared/NavMenu.razor @@ -1,6 +1,6 @@