上一篇搭建了 Blazor 项目并将总体框架改造了一下,本篇将完成用 C# 代码代替 JavaScript 实现几个小功能,说是代替但并不能彻底不用 JavaScript,应该说是尽可能不用吧。javascript
能够看到,当我鼠标移入的时候显示二维码,移出的时候隐藏二维码。css
这个功能若是是用JavaScript来完成的话,确定首先想到的是HTML的 Mouse 事件属性,那么在Blazor中也是同样的,给咱们实现了各类on*
事件。html
打开index.razor
页面,给微信图标那个 NavLink 标签添加两个事件,@onmouseover
和@onmouseout
。java
... <NavLink class="link-item weixin" title="扫码关注微信公众号:『阿星Plus』查看更多。" @onmouseover="Hover" @onmouseout="Hover"> <i class="iconfont iconweixin"></i> </NavLink> ...
当鼠标移入移出的时候都执行咱们自定义的一个方法Hover()
。git
C# 代码写在@code{}
花括号中,实现显示和隐藏原理是利用css,默认是隐藏的,当显示的时候将具备隐藏属性的class值去掉就能够了。github
因此,能够添加两个字段,一个用于判断当前是否处于隐藏状态,一个用来存储class的值。web
/// <summary> /// 是否隐藏 /// </summary> private bool IsHidden = true; /// <summary> /// 二维码CSS /// </summary> private string QrCodeCssClass => IsHidden ? "hidden" : null;
当IsHidden = true
,QrCodeCssClass = "hidden"
,当IsHidden = false
,QrCodeCssClass = null
。api
那么在Hover()
方法中,不断修改IsHidden
的值就能够实现效果了。浏览器
/// <summary> /// 鼠标移入移出操做 /// </summary> private void Hover() => IsHidden = !IsHidden;
最后将QrCodeCssClass
变量赋值给二维码图片所在的div上。缓存
... <div class="qrcode @QrCodeCssClass"> <img src="https://static.meowv.com/images/wx_qrcode.jpg" /> </div> ...
大功告成,index.razor
完整代码以下:
@page "/" <div class="main"> <div class="container"> <div class="intro"> <div class="avatar"> <a href="javascript:;"><img src="https://static.meowv.com/images/avatar.jpg"></a> </div> <div class="nickname">阿星Plus</div> <div class="description"> <p> 生命不息,奋斗不止 <br>Cease to struggle and you cease to live </p> </div> <div class="links"> <NavLink class="link-item" title="Posts" href="posts"> <i class="iconfont iconread"></i> </NavLink> <NavLink target="_blank" class="link-item" title="Notes" href="https://notes.meowv.com/"> <i class="iconfont iconnotes"></i> </NavLink> <NavLink target="_blank" class="link-item" title="API" href="https://api.meowv.com/"> <i class="iconfont iconapi"></i> </NavLink> <NavLink class="link-item" title="Manage" href="/account/auth"> <i class="iconfont iconcode"></i> </NavLink> <NavLink target="_blank" class="link-item" title="Github" href="https://github.com/Meowv/"> <i class="iconfont icongithub"></i> </NavLink> <NavLink class="link-item weixin" title="扫码关注微信公众号:『阿星Plus』查看更多。" @onmouseover="Hover" @onmouseout="Hover"> <i class="iconfont iconweixin"></i> </NavLink> <div class="qrcode @QrCodeCssClass"> <img src="https://static.meowv.com/images/wx_qrcode.jpg" /> </div> </div> </div> </div> </div> @code { /// <summary> /// 是否隐藏 /// </summary> private bool IsHidden = true; /// <summary> /// 二维码CSS /// </summary> private string QrCodeCssClass => IsHidden ? "hidden" : null; /// <summary> /// 鼠标移入移出操做 /// </summary> private void Hover() => IsHidden = !IsHidden; }
菜单是在小屏幕上才会出现的,相信看完了二维码的显示与隐藏,这个菜单的显示与隐藏就好办了吧,实现方法是同样的,菜单按钮是在头部组件Header.razor
中的,包括主题切换功能,因此下面代码都在Header.razor
里面。
@code { /// <summary> /// 下拉菜单是否打开 /// </summary> private bool collapseNavMenu = false; /// <summary> /// 导航菜单CSS /// </summary> private string NavMenuCssClass => collapseNavMenu ? "active" : null; /// <summary> /// 显示/隐藏 菜单 /// </summary> private void ToggleNavMenu() => collapseNavMenu = !collapseNavMenu; }
默认是不打开的,collapseNavMenu = false
。而后根据collapseNavMenu
值为NavMenuCssClass
给定不一样的class。
... <nav class="navbar-mobile"> <div class="container"> <div class="navbar-header"> <div> <NavLink class="menu-item" href="" Match="NavLinkMatch.All">😍阿星Plus</NavLink> <NavLink> · Light</NavLink> </div> <div class="menu-toggle" @onclick="ToggleNavMenu">☰ Menu</div> </div> <div class="menu @NavMenuCssClass"> <NavLink class="menu-item" href="posts">Posts</NavLink> <NavLink class="menu-item" href="categories">Categories</NavLink> <NavLink class="menu-item" href="tags">Tags</NavLink> <NavLink class="menu-item apps" href="apps">Apps</NavLink> </div> </div> </nav> ...
与二维码显示与隐藏惟一区别就是这里是点击按钮,不是移入移出,因此菜单显示与隐藏须要用到@onclick
方法。
哇,这个主题切换真的是一言难尽,当切换主题的时候须要记住当前的主题是什么,当刷新页面或者跳转其余页面的时候,主题状态是须要一致的,默认是白色主题,当切换暗黑色主题后实际上是在body上加了一个class。
在Blazor实在是不知道用什么办法去动态控制body的样式,因此这里我想到了一个办法,写几个全局的JavaScript方法,而后再Blazor中调用,要知道,他们是能够互相调用的,因而问题迎刃而解。
添加app.js
文件,放在 /wwwroot/js/ 下面。
var func = window.func || {}; func = { setStorage: function (name, value) { localStorage.setItem(name, value); }, getStorage: function (name) { return localStorage.getItem(name); }, switchTheme: function () { var currentTheme = this.getStorage('theme') || 'Light'; var isDark = currentTheme === 'Dark'; if (isDark) { document.querySelector('body').classList.add('dark-theme'); } else { document.querySelector('body').classList.remove('dark-theme'); } } };
这里写了三个方法,设置localStorage:setStorage(name,value)
,获取localStorage:getStorage(name)
,切换主题:switchTheme()
,localStorage 是浏览器以 name:value 形式的本地存储对象。
switchTheme
主要作的事情就是,判断当前主题若是是暗黑,就给body加上对应的class,若是不是就去掉。
而后在 index.html 中引用。
... <body> <app> <div class="loader"></div> </app> <script src="js/app.js"></script> <script src="_framework/blazor.webassembly.js"></script> </body> ...
有了这个三个全局的JavaScript方法,切换主题就变得简单多了,看代码。
... /// <summary> /// 当前主题 /// </summary> private string currentTheme; /// <summary> /// 初始化 /// </summary> /// <returns></returns> protected override async Task OnInitializedAsync() { currentTheme = await JSRuntime.InvokeAsync<string>("window.func.getStorage", "theme") ?? "Light"; await JSRuntime.InvokeVoidAsync("window.func.switchTheme"); } ...
注意在Blazor调用JavaScript方法须要注入IJSRuntime
接口,@inject IJSRuntime JSRuntime
。
新建一个变量currentTheme
,在生命周期函数初始化的时候去调用JavaScript中的getStorage
方法,获取当前主题,考虑到第一次访问的状况,能够给一个默认值为Light,表示白色主题,而后再去调用switchTheme,执行切换主题的方法。这样页面就会根据localStorage
的值来肯定当前的主题。
... /// <summary> /// 切换主题 /// </summary> private async Task SwitchTheme() { currentTheme = currentTheme == "Light" ? "Dark" : "Light"; await JSRuntime.InvokeVoidAsync("window.func.setStorage", "theme", currentTheme); await JSRuntime.InvokeVoidAsync("window.func.switchTheme"); } ...
SwitchTheme()
是切换主题的方法,当咱们点击input按钮时能够任意切换,而且主题还要实时跟着变化。
当点击按钮执行SwitchTheme()
时候改变currentTheme
的值,而后将currentTheme
传递给JavaScript方法setStorage
,最后再次执行切换主题的JavaScript方法便可。
此时变量currentTheme
也发挥了很多做用,在小屏幕下会显示当前主题的名称,Dark or Light,能够直接将currentTheme
在HTML中赋值便可。
而且咱们input是checkbox类型,当是黑色主题的时候须要时选中的状态,白色主题的时候不选中,这里就能够利用checked属性这样写:checked="@(currentTheme == "Dark")"
。
<nav class="navbar"> <div class="container"> ... <div class="menu navbar-right"> ... <input id="switch_default" type="checkbox" class="switch_default" @onchange="SwitchTheme" checked="@(currentTheme == "Dark")" /> <label for="switch_default" class="toggleBtn"></label> </div> </div> </nav> <nav class="navbar"> <div class="container"> ... <div class="menu navbar-right"> ... <input id="switch_default" type="checkbox" class="switch_default" @onchange="SwitchTheme" checked="@(currentTheme == "Dark")" /> <label for="switch_default" class="toggleBtn"></label> </div> </div> </nav> <nav class="navbar-mobile"> <div class="container"> <div class="navbar-header"> <div> <NavLink class="menu-item" href="" Match="NavLinkMatch.All">😍阿星Plus</NavLink> <NavLink @onclick="SwitchTheme"> · @currentTheme</NavLink> </div> <div class="menu-toggle" @onclick="ToggleNavMenu">☰ Menu</div> </div> <div class="menu @NavMenuCssClass"> ... </div> </div> </nav>
OK,搞定,快去试试吧。
如今看起来乱乱的,而且设置获取localStorage
属于公共的方法,说不定之后也能用到,咱们将其封装一下,便于往后的调用,否则要写好多重复的代码。
在Blazor项目根目录添加文件夹Commons,在文件夹下添加一个Common.cs
,目前用到了IJSRuntime
,用构造函数注入,而后写几个公共的方法。
//Common.cs using Microsoft.JSInterop; using System.Threading.Tasks; namespace Meowv.Blog.BlazorApp.Commons { public class Common { private readonly IJSRuntime _jsRuntime; public Common(IJSRuntime jsRuntime) { _jsRuntime = jsRuntime; } /// <summary> /// 执行无返回值方法 /// </summary> /// <param name="identifier"></param> /// <param name="args"></param> /// <returns></returns> public async ValueTask InvokeAsync(string identifier, params object[] args) { await _jsRuntime.InvokeVoidAsync(identifier, args); } /// <summary> /// 执行带返回值的方法 /// </summary> /// <typeparam name="TValue"></typeparam> /// <param name="identifier"></param> /// <param name="args"></param> /// <returns></returns> public async ValueTask<TValue> InvokeAsync<TValue>(string identifier, params object[] args) { return await _jsRuntime.InvokeAsync<TValue>(identifier, args); } /// <summary> /// 设置localStorage /// </summary> /// <param name="name"></param> /// <param name="value"></param> /// <returns></returns> public async Task SetStorageAsync(string name, string value) { await InvokeAsync("window.func.setStorage", name, value); } /// <summary> /// 获取localStorage /// </summary> /// <param name="name"></param> /// <returns></returns> public async Task<string> GetStorageAsync(string name) { return await InvokeAsync<string>("window.func.getStorage", name); } } }
而后须要在Program.cs
中注入。
using Meowv.Blog.BlazorApp.Commons; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.Extensions.DependencyInjection; using System; using System.Net.Http; using System.Threading.Tasks; namespace Meowv.Blog.BlazorApp { public class Program { public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("app"); builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); builder.Services.AddSingleton(typeof(Common)); await builder.Build().RunAsync(); } } }
紧接着在_Imports.razor
中注入使用Common
,@inject Commons.Common Common
。
改造一下Header.razor
,所有代码以下:
<header> <nav class="navbar"> <div class="container"> <div class="navbar-header header-logo"> <NavLink class="menu-item" href="/" Match="NavLinkMatch.All"> 😍阿星Plus </NavLink> </div> <div class="menu navbar-right"> <NavLink class="menu-item" href="posts">Posts</NavLink> <NavLink class="menu-item" href="categories">Categories</NavLink> <NavLink class="menu-item" href="tags">Tags</NavLink> <NavLink class="menu-item apps" href="apps">Apps</NavLink> <input id="switch_default" type="checkbox" class="switch_default" @onchange="SwitchTheme" checked="@(currentTheme == "Dark")" /> <label for="switch_default" class="toggleBtn"></label> </div> </div> </nav> <nav class="navbar-mobile"> <div class="container"> <div class="navbar-header"> <div> <NavLink class="menu-item" href="" Match="NavLinkMatch.All">😍阿星Plus</NavLink> <NavLink @onclick="SwitchTheme"> · @currentTheme</NavLink> </div> <div class="menu-toggle" @onclick="ToggleNavMenu">☰ Menu</div> </div> <div class="menu @NavMenuCssClass"> <NavLink class="menu-item" href="posts">Posts</NavLink> <NavLink class="menu-item" href="categories">Categories</NavLink> <NavLink class="menu-item" href="tags">Tags</NavLink> <NavLink class="menu-item apps" href="apps">Apps</NavLink> </div> </div> </nav> </header> @code { /// <summary> /// 下拉菜单是否打开 /// </summary> private bool collapseNavMenu = false; /// <summary> /// 导航菜单CSS /// </summary> private string NavMenuCssClass => collapseNavMenu ? "active" : null; /// <summary> /// 显示/隐藏 菜单 /// </summary> private void ToggleNavMenu() => collapseNavMenu = !collapseNavMenu; /// <summary> /// 当前主题 /// </summary> private string currentTheme; /// <summary> /// 初始化 /// </summary> /// <returns></returns> protected override async Task OnInitializedAsync() { currentTheme = await Common.GetStorageAsync("theme") ?? "Light"; await Common.InvokeAsync("window.func.switchTheme"); } /// <summary> /// 切换主题 /// </summary> private async Task SwitchTheme() { currentTheme = currentTheme == "Light" ? "Dark" : "Light"; await Common.SetStorageAsync("theme", currentTheme); await Common.InvokeAsync("window.func.switchTheme"); } }
实现过程比较简单,相信你绝对学会了。本篇就到这里了,未完待续...