Blazor组件自做三 : 使用JS隔离封装ZXing扫码
Blazor组件自做三 : 使用JS隔离封装ZXing扫码
本文基础步骤参考前两篇文章
Blazor组件自做一 : 使用JS隔离封装viewerjs库
Blazor组件自做二 : 使用JS隔离制作手写签名组件
1. 在文件夹wwwroot/lib,添加zxing子文件夹,里面下载库文件(文件文末源码里可复制) qrcode.min.js和zxing.min.js复制到此文件夹. 最终版本参考如下
+zxing
|-qrcode.min.js
|-zxing.min.js
2. 添加zxingjs.js文件
+zxing
|-zxingjs.js
zxingjs.js代码
import '/lib/zxing/zxing.min.js';
var codeReader = null;
export function init(autostop, wrapper, options) {
console.log('autostop' + autostop);
let selectedDeviceId;
//const codeReader = new ZXing.BrowserBarcodeReader()
codeReader = new ZXing.BrowserMultiFormatReader()
console.log('ZXing code reader initialized')
codeReader.getVideoInputDevices()
.then((videoInputDevices) => {
const sourceSelect = document.getElementById('sourceSelect')
selectedDeviceId = videoInputDevices[0].deviceId
console.log('videoInputDevices:' + videoInputDevices.length);
if (videoInputDevices.length > 1) {
videoInputDevices.forEach((element) => {
const sourceOption = document.createElement('option')
sourceOption.text = element.label
sourceOption.value = element.deviceId
sourceSelect.appendChild(sourceOption)
selectedDeviceId = element.deviceId;
})
sourceSelect.onchange = () => {
selectedDeviceId = sourceSelect.value;
codeReader.reset();
StartScan();
}
const sourceSelectPanel = document.getElementById('sourceSelectPanel')
sourceSelectPanel.style.display = 'block'
}
StartScan(autostop);
document.getElementById('startButton').addEventListener('click', () => {
StartScan();
})
function StartScan(autostop) {
codeReader.decodeOnceFromVideoDevice(selectedDeviceId, 'video').then((result) => {
console.log(result)
document.getElementById('result').textContent = result.text
var supportsVibrate = "vibrate" in navigator;
if (supportsVibrate) navigator.vibrate(1000);
if (autostop) {
console.log('autostop');
codeReader.reset();
return wrapper.invokeMethodAsync("invokeFromJS", result.text);
} else {
console.log('None-stop');
codeReader.reset();
wrapper.invokeMethodAsync("invokeFromJS", result.text);
}
}).catch((err) => {
console.error(err)
document.getElementById('result').textContent = err
})
console.log(`Started continous decode from camera with id ${selectedDeviceId}`)
}
document.getElementById('resetButton').addEventListener('click', () => {
document.getElementById('result').textContent = '';
codeReader.reset();
console.log('Reset.')
})
document.getElementById('closeButton').addEventListener('click', () => {
document.getElementById('result').textContent = '';
codeReader.reset();
console.log('closeButton.')
wrapper.invokeMethodAsync("invokeFromJSClose");
})
})
.catch((err) => {
console.error(err)
})
}
export function destroy(options) {
if (undefined !== codeReader && null !== codeReader && options.id == codeReader.element.id) {
codeReader.destroy();
console.log(codeReader.element.id, 'destroy');
}
}
3. 前面两篇文章主要在于快速入手建立组件, 基本没有解释代码. 现在开始穿插一点Blazor和JS交互的相关知识.
3.1 打开Day1的Pages/ViewerPage.razor文件,顶端有一句 @page "/viewer" , 意思是此页面的路由地址为viewer,请求此路径就会加载到此页面/组件.
参考阅读:ASP.NET Core Blazor 路由和导航
同理,Pages/HandwrittenPage.razor文件的 @page "/handwritten" 也是一样作用
3.2 调用组件演示页面代码逐行说明
直接调用Viewerjs组件,Images是组件的参数,打开文件Components/Viewerjs.razor可以查看定义
///
/// 图片列表
///
[Parameter] public List Images { get; set; } = new List();
@page "/viewer" //页面的路由地址
//调用Viewerjs组件,指定组件图片列表数据来源
@code{
List? imagesList;
protected override void OnInitialized() //组件初始化 , [参考阅读:组件生命周期](https://docs.microsoft.com/zh-cn/aspnet/core/blazor/components/lifecycle?view=aspnetcore-6.0)
{
//生成演示图片数据
imagesList = new List();
if (!imagesList.Any())
{
for (int i = 1; i <= 9; i++)
{
imagesList.Add($"https://fengyuanchen.github.io/viewerjs/images/thumbnails/tibet-{i}.jpg");
}
}
}
}
3.3 组件生命周期
参考阅读:组件生命周期
3.4 调整演示工程外观
现在Pages/Index.razor已经直接放置了两个占用空间比较大的组件,再加入今天的组件势必很难看,我们将对首页以及左侧导航菜单做一些调整,以便条理更加清晰.
删除Pages/Index.razor文件中以下代码
打开Shared/NavMenu.razor添加相关导航,NavLink组件是Blazor默认自带导航组件, 参考阅读:NavLink 类
5. 打开Components文件夹 , 新建BarcodeScanner.razor组件
参考阅读:Blazor JS 互操作
参考阅读:JS 模块中的 JS 隔离
5.1 C#组件实例封装
DotNetObjectReference.Create(this)
5.2 C#标记JS调用的函数[JSInvokable("invokeFromJS")]
[JSInvokable("invokeFromJS")]
public async Task ChangeValue(string val)
{
Result = val;
StateHasChanged();
await ScanResult.InvokeAsync(val);
}
5.3 zxingjs.js文件
import '/lib/zxing/zxing.min.js' //模块的方式加载zxing库
//定义一个函数init供blazor组件调用
export function init(autostop, wrapper, options) {} //wrapper为blazor组件的实例
//扫码结果通过blazor组件的实例调用DotNet.invokeMethodAsync实现
wrapper.invokeMethodAsync("invokeFromJS", result.text);
5.4 C#动态载入JS模块
private IJSObjectReference? module;
module = await JS.InvokeAsync("import", "./lib/zxing/zxingjs.js");
5.5 C#调用JS函数
module.InvokeVoidAsync("init", true, DotNetObjectReference.Create(this), null);
5.6 完整代码
BarcodeScanner.razor代码
@implements IAsyncDisposable
@namespace Blazor100.Components
@inject IJSRuntime JS
@Result
@code {
///
/// BarcodeScanner 条码扫描
///
///
/// 扫码按钮文本/Scan button title
///
[Parameter]
public string ScanBtnTitle { get; set; } = "扫码";
///
/// 复位按钮文本/Reset button title
///
[Parameter]
public string ResetBtnTitle { get; set; } = "复位";
///
/// 关闭按钮文本/Close button title
///
[Parameter]
public string CloseBtnTitle { get; set; } = "关闭";
///
/// 选择设备按钮文本/Select device button title
///
[Parameter]
public string SelectDeviceBtnTitle { get; set; } = "选择设备";
///
/// 扫码结果回调方法/Scan result callback method
///
[Parameter]
public EventCallback ScanResult { get; set; }
///
/// 关闭扫码框回调方法/Close scan code callback method
///
[Parameter]
public EventCallback Close { get; set; }
///
/// 扫码结果/Scan result
///
[Parameter]
public string? Result { get; set; }
///
/// 显示扫码框/Show scan box
///
[Parameter]
public bool ShowScanBarcode { get; set; }
private IJSObjectReference? module;
// To prevent making JavaScript interop calls during prerendering
protected override async Task OnAfterRenderAsync(bool firstRender)
{
try
{
if (!firstRender) return;
module = await JS.InvokeAsync("import", "./lib/zxing/zxingjs.js");
await module.InvokeVoidAsync("init", true, DotNetObjectReference.Create(this), null); //组件实例封装:DotNetObjectReference.Create(this)
}
catch (Exception e)
{
if (OnError != null) await OnError.Invoke(e.Message);
}
}
[JSInvokable("invokeFromJS")]
public async Task ChangeValue(string val)
{
Result = val;
StateHasChanged();
await ScanResult.InvokeAsync(val);
}
[JSInvokable("invokeFromJSClose")]
public async Task CloseScan()
{
await Close.InvokeAsync(null);
}
///
/// 获得/设置 错误回调方法
///
[Parameter]
public Func? OnError { get; set; }
async ValueTask IAsyncDisposable.DisposeAsync()
{
if (module is not null)
{
//await module.InvokeVoidAsync("destroy", Options);
await module.DisposeAsync();
}
}
}
6. Pages文件添加BarcodeScannerPage.razor文件,用于演示组件调用.
BarcodeScannerPage.razor代码
@page "/barcodescanner"
条码扫描 BarcodeReader
扫描条码/QR码。
@if (ShowScanBarcode)
{
}
@message
@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;
}
}
7. _Imports.razor加入一行引用组件的命名空间,已经有这行就不需要再重复写了.
@using Blazor100.Components
8. 首页引用组件演示页
或者Shared/NavMenu.razor添加导航
9. F5运行程序
至此,使用JS隔离封装ZXing扫码组件大功告成! Happy coding!
Blazor组件自做系列
Blazor组件自做四: 使用JS隔离封装signature_pad签名组件
Blazor组件自做五: 使用JS隔离封装Google地图
Blazor组件自做六: 使用JS隔离封装Baidu地图
Blazor组件自做七: 使用JS隔离制作定位/持续定位组件
Blazor组件自做八: 使用JS隔离封装屏幕键盘kioskboard.js组件