Blazor Web Assembly (WASM) 主题切换

介绍

本文讨论了从需求到实施的过程,以了解主题支持的实施和使用方式和方式。代码片段用于帮助撰写文章,而不是完整的代码转储。所有代码和示例项目都包含在下载中,以供进一步研究和试用。

本文假定您对Blazor有基本的了解,并提供指向您可能需要进一步信息和/或解释的各种外部资源的链接。

灵感

我想要一个具有现代动画效果的主题切换按钮。我喜欢Google Fonts网站上的按钮,但我是后端开发人员,而不是UI网页设计师。这是他们的按钮。

 (点击上图查看主题切换的工作原理)

下面是集成到此Blazor解决方案中的Kevin按钮。

(点击上图查看主题切换的工作原理)

设计

这个概念是:

  1. 最少的代码——完全包装或对自定义实现开放
  2. 开放式设计可与任何定制代码或CSS框架(如BootstrapTailwind等)一起使用。
  3. 可跨多个项目重用
  4. 如果不可避免,最少的JavaScript
  5. 最新的BlazorCSS3编码技术

实现

对于本文,使用了两种不同的主题切换方法:

1. 切换样式表

允许将主题明显分离到单独的文件中。这允许从Bootswatch等第三方网站下载主题。我在本文中使用了他们的Darkly(深色主题)和Flatly(浅色主题)。这是一种较旧的主题切换技术。

实现主题支持的最佳位置是尽可能靠近DOM的顶部。这是在MainLayout.razor中完成的。

对于样式表切换,我们需要更改页面head部分。为此,我们使用MainLayout.razor中的HeadContent组件:

<HeadContent>
    /* elements go here */
</HeadContent>

使用HeadContent组件时,顺序很重要。该组件将内容添加到页面head底部。

CSS样式表通常添加到index.html文件中。但是,为了切换样式表,我们从index.html 中删除颜色样式,并将文件放在MainLayout.razor文件中的<HeadContent>块中:

<HeadContent>
    <Themes>
        <DarkMode>
            <link href="css/bootstrap/darkly.min.css" rel="stylesheet" />
        </DarkMode>
        <LightMode>
            <link href="css/bootstrap/flatly.min.css" rel="stylesheet" />
        </LightMode>
    </Themes>
    <link href="css/app.css" rel="stylesheet" />
    <link href="ThemeByStylesheetDemo.styles.css" rel="stylesheet" />
</HeadContent>

应用程序CSS无需更改,因为每个主题文件中都应用了相同的CSS规则。这种方法侵入性最小,但是在切换主题时,浏览器刷新时页面可能会轻微闪烁。

2. CSS变量

CSS变量,也称为自定义属性,是当今网站中使用的现代推荐技术。像Open Props这样的CSS框架广泛使用CSS变量

主题切换是使用CSS完成的。在我们的例子中,我们默认为light模式,并添加一个CSS类名dark来切换模式。CSS标记看起来像:

:root {
    /* Light theme */
    --background: #fff;
    --font-color: #000;
    --font-color-2: #fff;
    --highlight: #f7f7f7;
    --highlight-2: #95a6a6;
    --link: #0366d6;
}

.dark {
    /* dark theme */
    --background: #222;
    --font-color: #fff;
    --font-color-2: #fff;
    --highlight: #393939;
    --highlight-2: #444444;
    --link: #3ca4ff;
}

使用CSS变量:

.page {
    background-color: var(--background);
    color: var(--font-color);
}

此方法确实需要使用CSS变量,因此需要更改现有样式表代码和内联样式规则,但好处是浏览器更新时不会闪烁。另一个好处是您现在使用共享变量和您的css更易于维护。

3. 主题切换

上面的动画示例,如谷歌字体网站,有一个用于手动切换的按钮。还有一个媒体查询,用于通过操作系统或网络浏览器检测用户更改。

所以通常在CSS中,我们会使用prefers-color-scheme媒体查询。

为此,我们需要监听change事件。Blazor目前无法直接监听媒体查询,因此我们需要使用一些带有回调的JavaScriptBlazor中。

这是JavaScript

function createThemeListener(dotNetRef) {
    window.matchMedia("(prefers-color-scheme: dark)").addListener(
        e => dotNetRef.invokeMethodAsync("DarkModeStateChanged", e.matches)
    );

Blazor中的初始化程序,我们通过回调传递对我们类的引用:

_jsRuntime = jsRuntime;
_moduleTask = new(() => jsRuntime.ModuleFactory(ScriptFile));

IJSObjectReference module = await _moduleTask!.Value;
DotNetInstance = DotNetObjectReference.Create(this);

以及对BlazorJavaScript事件的回调:

[JSInvokable]
public async Task DarkModeStateChanged(bool state)
    => await SetDarkModeAsync(state).ConfigureAwait(false);

javascript代码位于库中。使用最新版本的Blazor,可以包含javascript文件,而无需手动将其添加到index.html文件中。为此,我们export  javascript 函数。编译器看到了这一点,并为我们包含了javascript。所以更新后的javascript看起来像这样:

export function isDarkTheme() {
    return  window.matchMedia("(prefers-color-scheme: dark)").matches;
}

export function createThemeListener(dotNetRef) {
    window.matchMedia("(prefers-color-scheme: dark)").addListener(
        e => dotNetRef.invokeMethodAsync("DarkModeStateChanged", e.matches)
    );
}

export function getLocalStorage(key) {
    return localStorage[key];
}
export function setLocalStorage(key, value) {
    localStorage[key] = value;
}

您可以在Microsoft的文档中阅读有关其工作原理的更多信息。

4. 将按钮链接到开关

启用主题切换分为三个部分:

  1. 用户选择——在这种情况下,一个切换按钮。您还可以使用下拉列表或更定制的选择。
  2. MainLayout.razor文件中切换主题。
  3. 将选择链接到切换。我们将使用为此调用ThemeService的服务。

Dot Net Core使用IOC容器来实现依赖注入,以自动连接类及其依赖项。

ThemeService类通过ThemeToggle按钮组件或通过操作系统或浏览器更改来处理来自用户的共享主题状态和更改通知。所做的任何更改都会在MainLayout.razor组件中处理。

编码

包含示例项目以演示每种主题切换模式的工作原理。两个示例项目都使用包含的ThemeToggle组件,但是您可以使用自己的组件将其切换出来。

主题库

该库封装了所有核心功能以便于重用:

  • 在主项目中自动包含所有库CSSJavaScript

1. ThemeToggle组件

  • Aria compliant
  • BEM CSS类命名约定
  • 动画使用最少的动画
  • 亮或暗状态
  • 可选ShowTooltip属性
  • 自定义DarkTipMessageLightTipMessage属性
  • 支持16244348像素ButtonSize
  • 自定义Styleclassattribute
  • OnDarkModeStateChanged事件

@inject IThemeService themeService

<button @attributes="@Attributes"
        style="@Style"
        class="@GetComponentCssClass()"
        aria-label="@GetToolTip()"
        @onclick="_ => ToggleTheme()">
    <svg xmlns="http://www.w3.org/2000/svg"
         max-width="24px" max-height="24px"
         viewBox="0 0 472.39 472.39">
        <g class="theme-toggle__sun">
            <path d="M403.21,167V69.18H305.38L236.2,0,167,69.18H69.18V167L0,236.2l69.18,
             69.18v97.83H167l69.18,69.18,69.18-69.18h97.83V305.38l69.18-69.18Zm-167,
             198.17a129,129,0,1,1,129-129A129,129,0,0,1,236.2,365.19Z"/>
        </g>
        <g class="theme-toggle__circle">
            <circle cx="236.2" cy="236.2" r="103.78"/>
        </g>
    </svg>
</button>

按下按钮时,它会通知ThemeService

private void ToggleTheme()
    => themeService.DarkMode = !themeService.DarkMode;

2. ThemeService(核心)

  • DarkMode主题状态的属性——亮或暗
  • 将状态更改存储到浏览器localstorage中,以记住用户对页面重新加载、更改和以后网站重新访问的选择。
  • 侦听prefers -color-scheme媒体查询更改事件
  • 通知更改的OnDarkModeStateChanged事件

当对状态进行更改时,将执行以下代码:

private async Task SetDarkModeAsync(bool value)
{
    _darkMode = value;

    // store user's currently selected color scheme for the app
    await (await GetModuleInstance())
        .SetLocalStorageThemeAsync(_darkMode);

    OnDarkModeStateChanged?.Invoke(DarkMode);
}

SetLocalStorageThemeAsync是一个扩展方法,它包装了用于存储的JavaScript调用:

internal static class IJSObjectReferenceExtensions
{
    private static string JSSetLocalStorage = "setLocalStorage"; 

    public static async Task SetLocalStorageAsync(
        this IJSObjectReference? jsObjRef, string key, string value)
        => await jsObjRef!.InvokeVoidAsync(JSSetLocalStorage, key, value)
                          .ConfigureAwait(false);

    public static async Task SetLocalStorageThemeAsync(
        this IJSObjectReference? jsObjRef, bool IsDarkTheme)
        => await jsObjRef!.SetLocalStorageAsync(ThemeKey,IsDarkTheme
            ? DarkThemeValue : LightThemeValue).ConfigureAwait(false);
}

JavaScript

function setLocalStorage(key, value) {
    localStorage[key] = value;
}

3a 主题组件方法

该组件用于样式表切换:

  • 自动选择浅色或深色主题选择
  • 初始化ThemeService并监听OnDarkModeStateChanged事件以进行更改并引发渲染更新。

这是主题切换标记:

@if (ThemeService is not null && ThemeService.DarkMode)
{
    @if (DarkMode is not null)
    {
        @DarkMode
    }
}
else
{
    @if (LightMode is not null)
    {
        @LightMode
    }
}

监听并引发渲染更新的代码:

protected override async Task OnInitializedAsync()
{
    if (ThemeService is not null)
    {
        await ThemeService.InitializeAsync()!;
        ThemeService.OnDarkModeStateChanged+= OnDarkModeChanged;
    }

    await base.OnInitializedAsync();
}

private void OnDarkModeChanged(bool State) => StateHasChanged();

在您的应用程序中,MainLayout.razor中的组件的使用将是:

<HeadContent>
    <Theming.Themes>
        <DarkMode>
            <link href="css/bootstrap/darkly.min.css" rel="stylesheet" />
        </DarkMode>
        <LightMode>
            <link href="css/bootstrap/flatly.min.css" rel="stylesheet" />
        </LightMode>
    </Theming.Themes>
    <link href="css/app.css" rel="stylesheet" />
    <link href="ThemeTest.styles.css" rel="stylesheet" />
</HeadContent>

3b CSS类更改方法

CSS类选择手动连接MainLayout.razor

@inject IThemeService themeService

<div class="@GetClassCss()">

以及管理类的代码:

private bool IsDarkMode;

protected override async void OnInitialized()
{
    // uncomment if not using our ThemeToggle component
    //await themeService.InitializeAsync();
    themeService.OnDarkModeStateChanged += OnDarkModeChanged;
    await base.OnInitializedAsync();
}

private void OnDarkModeChanged(bool state)
{
    IsDarkMode = state;
    StateHasChanged();
}

private string GetClassCss()
    => "page" + (IsDarkMode ? " dark" : "");

测试主题切换

要测试浅色和深色主题之间的切换,您可以在WindowsMac OS中设置您的首选项,或使用Web浏览器中的设置/开发人员工具。下面,我列出了在常见浏览器中可以找到这些选项的位置。

Chrome/Edge

1、打开开发者工具

2、单击3个点以获取更多选项,然后单击更多工具,然后选择渲染

 3、向下滚动直到到达“prefers-color-scheme下拉选项。

FireFox

打开开发者工具Page Inspector,有按钮可以在浅色(太阳)和深色(月亮)模式之间切换。

Opera

选择最右侧的Easy Setup按钮,您可以在亮、暗和系统操作系统模式之间进行选择。

 总结

该库封装了管理主题状态和使用自动存储切换所需的所有功能,支持多种主题技术和现代切换。只需几行代码即可在您自己的项目中实现。

https://www.codeproject.com/Articles/5323228/Blazor-Web-Assembly-WASM-Theme-Switching

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值