Blazor 全屏按鈕/全屏服務 (BootstrapBlazor組件庫)


Blazor 簡介

Blazor 是一個使用 .NET 生成的交互式客戶端 Web UI 的框架。和前端同學所熟知的 Vue、React、Angular 有巨大差異。

其最大的特色是使用 C# 代碼(理論上可以是 .NET 生態的任何語言)代替 JavaScript 來實現邏輯。

  • 使用 C# 代替 JavaScript 來創建信息豐富的交互式 UI。
  • 共享使用 .NET 編寫的服務器端和客戶端應用邏輯。
  • 將 UI 呈現為 HTML 和 CSS,以支持眾多瀏覽器,其中包括移動瀏覽器。
  • 與新式托管平台(如 Docker)集成。
  • 使用 .NET 和 Blazor 生成混合桌面和移動應用。

使用 .NET 進行客戶端 Web 開發可提供以下優勢:

  • 使用 C# 代替 JavaScript 來編寫代碼。
  • 利用現有的 .NET 庫生態系統。
  • 在服務器和客戶端之間共享應用邏輯。
  • 受益於 .NET 的性能、可靠性和安全性。
  • 使用開發環境(例如 Visual Studio 或 Visual Studio Code)保持 Windows、Linux 或 macOS 上的工作效率。
  • 以一組穩定、功能豐富且易用的通用語言、框架和工具為基礎來進行生成。

有兩種不同開發模式

Blazor WebAssembly, C# 代碼運行在瀏覽器中。
Blazor Server,C# 代碼在服務器端執行,使用 SignalR 同步到瀏覽器進行更新。

Blazor 涉及技術

Blazor 是 apt.net core 生態的組成部分,所涉及到的技術也大部分和 .net 相關。

視圖層,使用 Razor 3 技術進行前端的編排渲染。Razor是一種標記語法,是 asp.net core 的默認視圖語法,最顯著的特點是強類型(C#、VB等)的代碼可以和 HTML 寫在一個文件中,當然也可以分開。在 razor 文件中,@符號后面的都是強類型語言,可以是一行中的一部分,也可以是一整行,還可以是一個段落。在 asp.net core 中的大致做法是把 VB、C# 等強類型語言嵌入到網頁,當網頁被請求的時候,在服務器端執行嵌入的代碼,動態生成頁面。

以 Blazor WebAssembly 開發方式運行時,依賴 WebAssembly 4 技術,可以做成靜態頁面不依賴任何后端服務器。

以 Blazor Server 方式開發運行時,依賴 SignalR 5 技術,並且需要后端服務器端配合。

Bootstrap 風格的 Blazor UI 組件庫 - BootstrapBlazor

https://www.blazor.zone/index

基於 Bootstrap 樣式庫精心打造,並且額外增加了 100 多種常用的組件,為您快速開發項目帶來非一般的感覺.

Element.requestFullscreen()

參考資料 https://developer.mozilla.org/zh-CN/docs/Web/API/Element/requestFullScreen

Element.requestFullscreen() 方法用於發出異步請求使元素進入全屏模式。

調用此API並不能保證元素一定能夠進入全屏模式。如果元素被允許進入全屏幕模式,返回的Promise會resolve,並且該元素會收到一個fullscreenchange (en-US)事件,通知它已經進入全屏模式。如果全屏請求被拒絕,返回的promise會變成rejected並且該元素會收到一個fullscreenerror (en-US)事件。如果該元素已經從原來的文檔中分離,那么該文檔將會收到這些事件。

早期的Fullscreen API實現總是會把這些事件發送給document,而不是調用的元素,所以你自己可能需要處理這樣的情況。參考 Browser compatibility in [Page not yet written] 來得知哪些瀏覽器做了這個改動。

注意:這個方法只能在用戶交互或者設備方向改變的時候調用,否則將會失敗。

語法

var Promise = Element.requestFullscreen(options);

參數

options 可選

一個FullscreenOptions (en-US)對象提供切換到全屏模式的控制選項。目前,唯一的選項是navigationUI (en-US),這控制了是否在元素處於全屏模式時顯示導航條UI。默認值是"auto",表明這將由瀏覽器來決定是否顯示導航條。

返回值

在完成切換全屏模式后,返回一個已經用值 undefined resolved的Promise

異常

requestFullscreen() 通過拒絕返回的 Promise來生成錯誤條件,而不是拋出一個傳統的異常。拒絕控制器接收以下的某一個值:

TypeError

在以下幾種情況下,會拋出TypeError:
文檔中包含的元素未完全激活,也就是說不是當前活動的元素。
元素不在文檔之內。
因為功能策略限制配置或其他訪問控制,元素不被允許使用"fullscreen"功能。
元素和它的文檔是同一個節點。

初步構建組件

1.建立js腳本

        bb_Fullscreen: function (ele) {
            ele.requestFullscreen() ||
                ele.webkitRequestFullscreen ||
                ele.mozRequestFullScreen ||
                ele.msRequestFullscreen;
        },
        bb_ExitFullscreen: function () {
            if (document.exitFullscreen) {
                document.exitFullscreen();
            }
            else if (document.mozCancelFullScreen) {
                document.mozCancelFullScreen();
            }
            else if (document.webkitExitFullscreen) {
                document.webkitExitFullscreen();
            }
            else if (document.msExitFullscreen) {
                document.msExitFullscreen();
            }
        }
    });

2.建立Razor頁面測試

以下為簡化代碼,運行測試一下功能是否達到需求.

<button @onclick="FullScreen">全屏</button>
<button @onclick="ExitFullScreen">退出全屏</button>

@code{
[Inject] IJSRuntime? JSRuntime{ get; set; }

  //進入全屏
  private Task FullScreen() => await JSRuntime.InvokeVoidAsync("bb_Fullscreen");

  //退出全屏
  private Task ExitFullScreen() => await JSRuntime.InvokeVoidAsync("bb_ExitFullscreen");
}

3.優化邏輯,添加單按鈕全屏切換邏輯,添加針對單獨元素的全屏邏輯

JS完整代碼

(function ($) {
    $.extend({
        bb_toggleFullscreen: function (el, id) {
            var ele = el;
            if (!ele || ele === '') {
                if (id) {
                    ele = document.getElementById(id);
                }
                else {
                    ele = document.documentElement;
                }
            }
            if ($.bb_IsFullscreen()) {
                $.bb_ExitFullscreen();
                ele.classList.remove('fs-open');
            }
            else {
                $.bb_Fullscreen(ele);
                ele.classList.add('fs-open');
            }
        },
        bb_Fullscreen: function (ele) {
            ele.requestFullscreen() ||
                ele.webkitRequestFullscreen ||
                ele.mozRequestFullScreen ||
                ele.msRequestFullscreen;
        },
        bb_ExitFullscreen: function () {
            if (document.exitFullscreen) {
                document.exitFullscreen();
            }
            else if (document.mozCancelFullScreen) {
                document.mozCancelFullScreen();
            }
            else if (document.webkitExitFullscreen) {
                document.webkitExitFullscreen();
            }
            else if (document.msExitFullscreen) {
                document.msExitFullscreen();
            }
        },
        bb_IsFullscreen: function () {
            return document.fullscreen ||
                document.webkitIsFullScreen ||
                document.webkitFullScreen ||
                document.mozFullScreen ||
                document.msFullScreent;
        }
    });
})(jQuery);

測試功能

<button @onclick="ToggleFullScreen">全屏</button>

@code{
[Inject] IJSRuntime? JSRuntime{ get; set; }

  //全屏方法,已經全屏時再次調用后退出全屏
  private Task ToggleFullScreen() => await JSRuntime.InvokeVoidAsync("bb_toggleFullscreen");
}

4.封裝為服務

再次進行思考,如果將一顆按鈕封裝為組件,那只有UI界面才能調用,而且式樣什么的都不算最靈活,為何不做成一個服務,與UI分開解耦呢? 別着急, 馬上開干.

我作為一個blazor愛好者,每一個想法,轉化為一個組件后,是值得提交到例如BootstrapBlazor之類組件庫大家一起學習一起進步的,自從我2020-09-25把ZXingBlazor組件提交到BootstrapBlazor之后,從自嗨到團隊合作,真的學習到了很多知識和技巧,在學習BB的源碼的過程中,深刻體會到了那句話的精髓:"每入一寸就有一寸的驚喜!".

項目負責人Argo作為一位微軟MVP和業內人士,對整個微軟技術棧有很深刻的認識和思考,對我本人更是幫助巨大,在此謝謝Argo, 😃

最后版本代碼已經提交為組件庫里面的一個組件,所以有些代碼繼承了組件庫的功能,如果運行跟默認Blazor工程有不一致的地方,大家可以Fork到自己倉庫去試驗,以下文章不再贅述.

構建服務 FullScreenService.cs

using Microsoft.AspNetCore.Components;

namespace BootstrapBlazor.Components;

/// <summary>
/// FullScreen 服務
/// </summary>
public class FullScreenService : BootstrapServiceBase<FullScreenOption>
{
    /// <summary>
    /// 全屏方法,已經全屏時再次調用后退出全屏
    /// </summary>
    /// <param name="option"></param>
    /// <returns></returns>
    public Task Toggle(FullScreenOption? option = null) => Invoke(option ?? new());

    /// <summary>
    /// 通過 ElementReference 將指定元素進行全屏
    /// </summary>
    /// <param name="element"></param>
    /// <returns></returns>
    public Task ToggleByElement(ElementReference element) => Invoke(new() { Element = element });

    /// <summary>
    /// 通過元素 Id 將指定元素進行全屏
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public Task ToggleById(string id) => Invoke(new() { Id = id });
}

全屏服務類 FullScreenOption.cs

using Microsoft.AspNetCore.Components;

namespace BootstrapBlazor.Components;

/// <summary>
/// FullScreen 配置類
/// </summary>
public class FullScreenOption
{
    /// <summary>
    /// 
    /// </summary>
    public ElementReference Element { get; set; }

    /// <summary>
    /// 
    /// </summary>
    public string? Id { get; set; }
}

注冊服務

 services.TryAddScoped<FullScreenService>();

FullScreen.cs

using Microsoft.AspNetCore.Components;

namespace BootstrapBlazor.Components;

/// <summary>
/// FullScreen 組件部分類
/// </summary>
public class FullScreen : BootstrapComponentBase, IDisposable
{
    /// <summary>
    /// DialogServices 服務實例
    /// </summary>
    [Inject]
    [NotNull]
    private FullScreenService? FullScreenService { get; set; }

    /// <summary>
    /// OnInitialized 方法
    /// </summary>
    protected override void OnInitialized()
    {
        base.OnInitialized();

        // 注冊 FullScreen 彈窗事件
        FullScreenService.Register(this, Show);
    }

    /// <summary>
    /// OnAfterRenderAsync 方法
    /// </summary>
    /// <param name="firstRender"></param>
    /// <returns></returns>
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);

        if (Option != null)
        {
            await JSRuntime.InvokeVoidAsync(Option.Element.Context != null ? Option.Element : "", "bb_toggleFullscreen", Option.Id ?? "");
            Option = null;
        }
    }

    private FullScreenOption? Option { get; set; }

    private Task Show(FullScreenOption option)
    {
        Option = option;
        StateHasChanged();
        return Task.CompletedTask;
    }

    /// <summary>
    /// Dispose 方法
    /// </summary>
    /// <param name="disposing"></param>
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            FullScreenService.UnRegister(this);
        }
    }

    /// <summary>
    /// Dispose 方法
    /// </summary>
    public void Dispose()
    {
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }
}

組件方式調用 [簡化版] FullScreenButton.Razor

@namespace BootstrapBlazor.Components
@inherits TooltipComponentBase

<a @attributes="AdditionalAttributes" id="@Id" class="@ClassString" @onclick="ToggleFullScreen">
    <i class="@ButtonIconString"></i>
    <i class="@FullScreenIconString"></i>
</a>

<CascadingValue Value="this" IsFixed="true">
    <Tooltip Title="@Title" />
</CascadingValue>

@code{ 

    [Inject]
    [NotNull]
    private FullScreenService? FullScrenService { get; set; }

    private Task ToggleFullScreen() => FullScrenService.Toggle();
}

5.FullScreens 全屏示例代碼

Razor

@page "/fullscreens"
@inject IStringLocalizer<FullScreens> Localizer

<h3>@Localizer["Title"]</h3>

<h4>@((MarkupString)Localizer["H1"].Value)</h4>

<DemoBlock Title="@Localizer["Block1Title"]" Introduction="@Localizer["Block1Intro"]" Name="Normal">
    <Button Text="@Localizer["ButtonText1"]" OnClick="ToggleFullScreen"></Button>
</DemoBlock>

<DemoBlock Title="@Localizer["Block2Title"]" Introduction="@Localizer["Block2Intro"]" Name="Title">
    <ul class="ul-demo mb-3">
        <li>@((MarkupString)Localizer["Li1"].Value)</li>
        <li>@((MarkupString)Localizer["Li2"].Value)</li>
    </ul>
    <FullScreenButton Title="@Localizer["Button1Text"]" FullScreenIcon="fa fa-fa" />
    <Pre class="mt-3">&lt;@Localizer["Pre"]" /&gt;</Pre>
</DemoBlock>

cs代碼

using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components;

namespace BootstrapBlazor.Shared.Samples;

/// <summary>
/// FullScreens 全屏示例代碼
/// </summary>
public partial class FullScreens
{
    [Inject]
    [NotNull]
    private FullScreenService? FullScreenService { get; set; }

    private async Task ToggleFullScreen()
    {
        await FullScreenService.Toggle();
    }
}

最終版本

BootstrapBlazor提交組件簡單步驟

示例文檔

  1. 添加對應組件中文資源到 BootstrapBlazor.Shared/Locales/zh.json 文件
"BootstrapBlazor.Shared.Pages.Coms": {
  ...
  "FullScreenText": "全屏組件 FullScreen",
  ...
},
"BootstrapBlazor.Shared.Samples.FullScreens": {
  "Title": "FullScreen 全屏",
  "Block1Title": "基本用法",
  ...
}
  1. 添加對應組件中文資源到 BootstrapBlazor.Shared/Locales/en.json 文件
"BootstrapBlazor.Shared.Pages.Coms": {
  ...
  "FullScreenText": "FullScreen",
  ...
},
"BootstrapBlazor.Shared.Samples.FullScreens": {
  "Title": "FullScreen",
  "Block1Title": "Basic usage",
  ...
}
  1. 添加示例到"組件" 頁面

    BootstrapBlazor.Shared/Pages/Coms.razor文件,找到某個組件大類別,例如導航組件 <ComponentCategory Text="@Localizer["Text2"]">

<ComponentCategory Text="@Localizer["Text2"]">
  ...
  <ComponentCard Text="@Localizer["FullScreenText"]" Image="FullScreen.jpg" Url="fullscreens"></ComponentCard>
  ...
</ComponentCategory>
  1. BootstrapBlazor.Shared/docs.json添加
"fullscreens": "FullScreens",
  1. NavMenu.razor
    private void AddNavigation(DemoMenuItem item)
    {
        item.Items = new List<DemoMenuItem>
        {
            ...
            new()
            {
                IsNew = true,
                Text = Localizer["FullScreen"],
                Url = "fullscreens"
            },
            ...
        };

        AddBadge(item);
    }
  1. 示例文件 BootstrapBlazor.Shared/Samples/FullScreens.razor
@page "/fullscreens"
@inject IStringLocalizer<FullScreens> Localizer

<h3>@Localizer["Title"]</h3>

<DemoBlock Title="@Localizer["Block1Title"]" Introduction="@Localizer["Block1Intro"]" Name="Normal">
    <Button Text="@Localizer["ButtonText1"]" OnClick="ToggleFullScreen"></Button>
</DemoBlock>

<DemoBlock Title="@Localizer["Block2Title"]" Introduction="@Localizer["Block2Intro"]" Name="Title">
    <FullScreenButton Title="@Localizer["Button1Text"]" FullScreenIcon="fa fa-fa" />
</DemoBlock>
  1. 示例文件 BootstrapBlazor.Shared/Samples/FullScreens.razor.cs
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components;

namespace BootstrapBlazor.Shared.Samples;

/// <summary>
/// FullScreens 全屏示例代碼
/// </summary>
public partial class FullScreens
{
    [Inject]
    [NotNull]
    private FullScreenService? FullScreenService { get; set; }

    private async Task ToggleFullScreen()
    {
        await FullScreenService.Toggle();
    }
}

參考資料

ASP.NET Core Blazor https://docs.microsoft.com/zh-cn/aspnet/core/blazor/?view=aspnetcore-6.0
五分鍾了解 Blazor https://segmentfault.com/a/1190000040800253
Element.requestFullscreen() https://developer.mozilla.org/zh-CN/docs/Web/API/Element/requestFullScreen
Bootstrap 風格的 Blazor UI 組件庫 https://www.blazor.zone/index
!1821 feat(#I48WXD): add FullScreen component https://gitee.com/LongbowEnterprise/BootstrapBlazor/commit/30caa995eba38e91d15b8a5465c6c9c738db068f

項目源碼

Github | Gitee

關聯項目

BA & Blazor QQ群:795206915、675147445

知識共享許可協議

本作品采用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名AlexChow(包含鏈接: https://github.com/densen2014 ),不得用於商業目的,基於本文修改后的作品務必以相同的許可發布。如有任何疑問,請與我聯系

AlexChow

今日頭條 | 博客園 | 知乎 | Gitee | GitHub


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM