Maui Blazor 使用摄像头实现

maui,blazor,使用,摄像头,实现 · 浏览次数 : 779

小编点评

## MauiBlazorWebViewHandler 的代码解析 这个代码展示了如何使用 WebView 在 Android、PC 和 iOS 平台上实现摄像头权限并截取照片的功能。 **主要代码解析:** 1. **`MauiWebChromeClient`** 类: * 继承自 `Android.Webkit.WebView.Maui` 类,负责与 WebView 之间的通信。 * 在 `ConnectHandler` 方法中设置 WebView 的 Chrome 客户端。 2. **`MauiBlazorWebViewHandler`** 类: * 实现 `BlazorWebViewHandler` 接口。 * 在 `OnInitializedAsync` 方法中初始化 WebView 的 ID 和属性。 * 在 `OpenCamera` 方法中检查摄像头是否打开,如果未打开,就调用 JavaScript 方法 `startVideo` 启动摄像头。 * 在 `Screenshot` 方法中调用 `JSRuntime` 调用 `setImgSrc` 方法以截取照片。 3. **`setImgSrc` 方法**: * 使用 `JSRuntime` 调用 JavaScript 方法 `setImgSrc` 来设置 CanvasID、视频ID 和截图宽度和高度。 4. **`OnInitializedAsync` 方法**: * 设置一些全局变量,用于存储 CanvasID、视频ID 和截图宽度和高度。 5. **`OpenCamera` 方法**: * 检查摄像头是否打开,如果未打开,则调用 JavaScript 方法 `startVideo` 启动摄像头。 * 设置 `OpenCameraStatus` 为 `true`,表示摄像头已打开。 * 通过 `StateHasChanged` 通知 UI,更新 UI 显示摄像头已打开。 6. **`Screenshot` 方法**: * 使用 `JSRuntime` 调用 `setImgSrc` 方法设置 CanvasID、视频ID 和截图宽度和高度。 **其他细节:** * 代码示例中使用了 `Guid` 来生成唯一的 ID,这可能是一种安全方法来避免重复使用相同 ID 的问题。 * `JSRuntime` 用于执行 JavaScript 代码,这可以用于动态设置 CanvasID、视频ID 和截图宽度和高度。 * 代码使用 `async` 和 `await` 语句确保线程安全,并在 UI 上进行操作。

正文

Maui Blazor 使用摄像头实现

由于Maui Blazor中界面是由WebView渲染,所以再使用Android的摄像头时无法去获取,因为原生的摄像头需要绑定界面组件
所以我找到了其他的实现方式,通过WebView使用js调用设备摄像头 支持多平台兼容目前测试了Android 和PC 由于没有ioc和mac无法测试 大概率是兼容可能需要动态申请权限

1. 添加js方法

我们再wwwroot中创建一个helper.js的文件并且添加以下俩个js函数
index.html中添加<script src="helper.js"></script>引入js


/**
 * 设置元素的src
 * @param {any} canvasId canvasId的dom id
 * @param {any} videoId video的dom id
 * @param {any} srcId img的dom id
 * @param {any} widht 设置截图宽度
 * @param {any} height 设置截图高度
 */
function setImgSrc(dest,videoId, srcId, widht, height) {
    let video = document.getElementById(videoId);
    let canvas = document.getElementById(canvasId);
    console.log(video.clientHeight, video.clientWidth);
   
    canvas.getContext('2d').drawImage(video, 0, 0, widht, height);

    canvas.toBlob(function (blob) {
        var src = document.getElementById(srcId);
        src.setAttribute("height", height)
        src.setAttribute("width", widht);
        src.setAttribute("src", URL.createObjectURL(blob))
    }, "image/jpeg", 0.95);
}


/**
 * 初始化摄像头
 * @param {any} src 
 */
function startVideo(src) {
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
        navigator.mediaDevices.getUserMedia({ video: true }).then(function (stream) {
            let video = document.getElementById(src);
            if ("srcObject" in video) {
                video.srcObject = stream;
            } else {
                video.src = window.URL.createObjectURL(stream);
            }
            video.onloadedmetadata = function (e) {
                video.play();
            };
            //mirror image
            video.style.webkitTransform = "scaleX(-1)";
            video.style.transform = "scaleX(-1)";
        });
    }
}

然后各个平台的兼容

android:

Platforms/Android/AndroidManifest.xml文件内容

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
	<application android:allowBackup="true" android:supportsRtl="true"></application>
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	<uses-permission android:name="android.permission.INTERNET" />
	<!--相机权限-->
	<uses-permission android:name="android.permission.CAMERA" android:required="false"/>
	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
	<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
	<!--相机功能-->
	<uses-feature android:name="android.hardware.camera" />
	<!--音频录制权限-->
	<uses-permission android:name="android.permission.RECORD_AUDIO" />
	<!--位置权限-->
	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
	
	<!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
	<uses-feature android:name="android.hardware.location.gps" />

	<queries>
		<intent>
			<action android:name="android.intent.action.VIEW" />
			<data android:scheme="http"/>
		</intent>
		<intent>
			<action android:name="android.intent.action.VIEW" />
			<data android:scheme="https"/>
		</intent>
	</queries>
</manifest>

Platforms/Android/MainActivity.cs文件内容

[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
public class MainActivity : MauiAppCompatActivity
{
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        Platform.Init(this, savedInstanceState);
        // 申请所需权限 也可以再使用的时候去申请
        ActivityCompat.RequestPermissions(this, new[] { Manifest.Permission.Camera, Manifest.Permission.RecordAudio, Manifest.Permission.ModifyAudioSettings }, 0);
    }
}

MauiWebChromeClient.cs文件内容

#if ANDROID
using Android.Webkit;
using Microsoft.AspNetCore.Components.WebView.Maui;

namespace MainSample;

public class MauiWebChromeClient : WebChromeClient
{
    public override void OnPermissionRequest(PermissionRequest request)
    {
        request.Grant(request.GetResources());
    }
}

public class MauiBlazorWebViewHandler : BlazorWebViewHandler
{
    protected override void ConnectHandler(Android.Webkit.WebView platformView)
    {
        platformView.SetWebChromeClient(new MauiWebChromeClient());
        base.ConnectHandler(platformView);
    }
}

#endif

MauiProgram.cs中添加以下代码;如果没有下面代码会出现没有摄像头权限
具体在这个 issue体现


#if ANDROID
        builder.ConfigureMauiHandlers(handlers =>
        {
            handlers.AddHandler<IBlazorWebView, MauiBlazorWebViewHandler>();
        });
#endif

以上是android的适配代码 pc不需要设置额外代码 ios和mac不清楚

然后编写界面

@page "/" @*界面路由*@

@inject IJSRuntime JSRuntime @*注入jsRuntime*@

@if(OpenCameraStatus) @*在摄像头没有被打开的情况不显示video*@
{
    <video id="@VideoId" width="@widht" height="@height" />
    <canvas class="d-none" id="@CanvasId" width="@widht" height="@height" />
}
<button @onclick="" style="margin:8px">打开摄像头</button> 
@*因为摄像头必须是用户手动触发如果界面是滑动进入无法直接调用方法打开摄像头所以添加按钮触发*@
<button style="margin:8px">截图</button> @*对视频流截取一张图*@

<img id="@ImgId" />

@code{
    private string CanvasId;
    private string ImgId;
    private string VideoId;
    private bool OpenCameraStatus;
    private int widht = 320;
    private int height = 500;

    private async Task OpenCamera()
    {
        if (!OpenCameraStatus)
        {
            // 由于打开摄像头的条件必须是用户手动触发如果滑动切换到界面是无法触发的
            await JSRuntime.InvokeVoidAsync("startVideo", "videoFeed");
            OpenCameraStatus = true;
            StateHasChanged();
        }
    }

    protected override async Task OnInitializedAsync()
    {
        // 初始化id
        ImgId = Guid.NewGuid().ToString("N");
        CanvasId = Guid.NewGuid().ToString("N");
        VideoId = Guid.NewGuid().ToString("N");
        await base.OnInitializedAsync();
    }

    private async Task Screenshot()
    {
        await JSRuntime.InvokeAsync<String>("setImgSrc", CanvasId,VideoId, ImgId, widht, height);
    }
}

然后可以运行程序就可以看到我们的效果了

示例代码:gitee github

来着token的分享
微信交流群:

技术交流群:737776595

与Maui Blazor 使用摄像头实现相似的内容:

Maui Blazor 使用摄像头实现

Maui Blazor 使用摄像头实现 由于Maui Blazor中界面是由WebView渲染,所以再使用Android的摄像头时无法去获取,因为原生的摄像头需要绑定界面组件 所以我找到了其他的实现方式,通过WebView使用js调用设备摄像头 支持多平台兼容目前测试了Android 和PC 由于没

Maui 读取外部文件显示到Blazor中

Maui 读取外部文件显示到Blazor中 首先在maui blazor中无法直接读取外部文件显示 ,但是可以通过base64去显示 但是由于base64太长可能影响界面卡顿 这个时候我们可以使用blob链接去加载外部图片 它不需要copy文件到wwwroot中 它会将byte转换一个url供bla

.NET 8 Preview 6发布,支持新的了Blazor呈现方案 和 VS Code .NET MAUI 扩展

2023年7月11日 .NET 8 Preview 6,.NET 团队在官方博客发布了系列文章:Announcing .NET 8 Preview 6[1]ASP.NET Core updates in .NET 8 Preview 6[2]Announcing .NET MAUI in .NET

一个 .NET 开源的地图组件库 - Mapsui

前言 今天大姚给大家分享一个.NET开源(MIT License)、免费、同时支持多平台框架(MAUI、WPF、Avalonia、Uno、Blazor、WinUI、Eto、.NET Android 和 .NET iOS)地图组件库:Mapsui。 项目源代码 支持的UI框架的NuGet包 创建Bla

5款.NET开源、免费、功能强大的图表库

LiveCharts2 LiveCharts2是一个.NET开源(MIT License)、简单、灵活、交互式且功能强大的.NET图表、地图和仪表,现在几乎可以在任何地方运行如:Maui、Uno Platform、Blazor-wasm、WPF、WinForms、Xamarin、Avalonia、W

[MAUI 项目实战] 笔记App(二):数据库设计

@目录Sqlite配置创建实体笔记实体类笔记分组实体笔记片段实体笔记片段负载实体笔记片段仓库实体笔记模板(场景)实体笔记片段模板实体笔记片段模板负载实体配置EF创建映射迁移和种子数据项目地址 Sqlite配置 应用程序里使用Sqlite作为数据库,使用EntityFramworkCore作为ORM,

[MAUI 项目实战] 笔记App(一):程序设计

前言 有人说现在记事类app这么多,市场这么卷,为什么还想做一个笔记类App? 一来,去年小孩刚出生,需要一个可以记录喂奶时间的app,发现市面上没有一款app能够在两步内简单记录一个时间,可能iOS可以通过备忘录配合捷径做到快速记录,但是安卓上就没有类似的app。 二是,自去年做的音乐播放器以来,

分享下最近基于Avalonia UI和MAUI写跨平台时间管理工具的体验

起因 几个月前,我在寻找一款时间管理软件,类似番茄时钟的工具,但是希望可以自定义时间。 需要自定义的场景 做雅思阅读,3篇文件需要严格控制时间分配,需要一个灵活的计时器 定期提醒,每30分钟需要喝水或者上个厕所或者摸一下鱼... 总结起来就是:专注一段时间,比如30分钟,然后休息10分钟,且没有杂七

在.NET 8 RC1 版本中 MAUI、ASP.NET Core 和 EF8 的新特性

从年初2 月份发布第一个预览版,经历7个预览版后,Microsoft 西雅图时间9月13日发布了 .NET 8 RC 1: https://devblogs.microsoft.com/dotnet/announcing-dotnet-8-rc1/[1], 这是该框架在 11 月初正式发布之前的两个

.NET周刊【9月第3期 2023-09-17】

国内文章 在.NET 8 RC1 版本中 MAUI、ASP.NET Core 和 EF8 的新特性 https://www.cnblogs.com/shanyou/p/17698428.html 从年初2 月份发布第一个预览版,经历7个预览版后,Microsoft 西雅图时间9月13日发布了 .NE