想到動畫,你可能會去安裝Blazor的動畫組件BlazorAnimate,然後使用它。本人初學,暫時我也不知道原理,先不用組件,自己實現吧。雖然項目中我用了AntDesignBlazor,但是我忘了使用它的菜單組件,我用的菜單組件還是VS2022自動生成的,後來我把這個菜單改造了一下,支持多級菜單, ...
想到動畫,你可能會去安裝Blazor的動畫組件BlazorAnimate,然後使用它。本人初學,暫時我也不知道原理,先不用組件,自己實現吧。雖然項目中我用了AntDesignBlazor,但是我忘了使用它的菜單組件,我用的菜單組件還是VS2022自動生成的,後來我把這個菜單改造了一下,支持多級菜單,加了展示收縮箭頭,那就在這基礎上做吧。
1. 引用jQuery
這裡使用jquery的animate方法實現動畫
在wwwroot/js目錄放一個jquery-1.9.1.js文件,然後在html(或_Layout.cshtml文件)中引入該js
<script src="js/jquery-1.9.1.js"></script>
2. 為左側菜單組件NavMenu.razor添加一個js文件:NavMenu.razor.js
內容如下:
export function animate(index) { //index是菜單編碼 2位數是一級菜單,4位數是二級菜單,以此類推
let time = 200;
let content = $(".content" + index);
let h = content.height() + "px";
content.css("overflow", "hidden");
if (index <= 99 || content.hasClass("collapse")) { //index<=99表示一級菜單,一級菜單隻有展開動畫,沒有收縮動畫;content.hasClass("collapse")表示當前是收縮狀態。
content.css("height", "0");
//展開動畫
content.animate({
height: h
}, time, "linear", () => {
content.css("height", "auto");
});
} else { //不是一級菜單並且當前是展開狀態,將執行收縮動畫
content.css("height", "auto");
//收縮動畫
content.animate({
height: 0
}, time, "linear", () => {
setTimeout(function () { //延遲執行,否則會導致閃爍
content.css("height", "auto");
}, 100);
});
return [time]; //收縮時,需要等待收縮動畫展示完成,再隱藏菜單容器div
}
return [0];
}
3. 在Blazor組件NavMenu.razor文件中引入該js
@inject IJSRuntime _js;
@code {
IJSObjectReference _module;
protected override async void OnAfterRender(bool firstRender)
{
if (firstRender)
{
_module = await _js.InvokeAsync<IJSObjectReference>("import", "./Shared/NavMenu.razor.js");
}
}
}
4. 調用js方法實現菜單動畫
@code{
...省略此處的代碼
private async Task ToggleMenuClick(int index)
{
object[] args = new object[] { index };
object[] objs = await _module.InvokeAsync<object[]>("animate", args);
int time = ((JsonElement)objs[0]).GetInt32();
if (time > 0) { await Task.Delay(time); } //time大於0,表示需要等待收縮動畫展示完成,再隱藏菜單容器div
if (index > 99)
{
if (_dict.ContainsKey(index))
{
_dict[index] = !_dict[index]; //記錄非一級菜單的展開狀態
}
else
{
_dict[index] = false; //記錄非一級菜單的展開狀態
}
}
else
{
_index = index; //_index是當前展示的菜單編碼
}
}
...省略此處的代碼
}
效果圖
NavMenu.razor頁面完整代碼如下
@using System.Text.Json;
@inject IJSRuntime _js;
<!-- 菜單組 -->
<div class="top-row ps-3 navbar navbar-dark" @onclick="@(async ()=>await ToggleMenuClick(01))">
<div class="container-fluid">
<a class="navbar-brand" href="javascript:void(0);">任務管理</a>
<button title="Navigation menu" class="navbar-toggler">
<span class="navbar-toggler-icon"></span>
</button>
<div class="@ArrowClass(01)"></div>
</div>
</div>
<!-- 菜單組內容 -->
<div class="@MenuGroupContentClass(01)">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="kpTask/taskList">
<span class="oi oi-list" aria-hidden="true"></span> 任務列表
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="kpTask/taskRunList">
<span class="oi oi-list" aria-hidden="true"></span> 任務執行記錄
</NavLink>
<!-- 二級菜單組 -->
<NavLink class="nav-link" href="javascript:void(0);" @onclick="@(()=> ToggleMenuClick(0101))" style="display:;">
<span class="oi oi-list" aria-hidden="true"></span> 測試二級菜單組
<div class="@ArrowClass(0101)"></div>
</NavLink>
<!-- 二級菜單組內容 -->
<div class="@MenuGroupContentClass(0101)" style="display:;">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-list" aria-hidden="true"></span> 測試二級菜單
</NavLink>
<!-- 三級菜單組 -->
<NavLink class="nav-link" href="javascript:void(0);" @onclick="@(()=> ToggleMenuClick(010101))" style="display:;">
<span class="oi oi-list" aria-hidden="true"></span> 測試三級菜單組
<div class="@ArrowClass(010101)"></div>
</NavLink>
<!-- 三級菜單組內容 -->
<div class="@MenuGroupContentClass(010101)" style="display:;">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-list" aria-hidden="true"></span> 測試三級菜單
</NavLink>
</div>
</nav>
</div>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-list" aria-hidden="true"></span> 測試二級菜單2
</NavLink>
</div>
</nav>
</div>
</div>
</nav>
</div>
<!-- 菜單組 -->
<div class="top-row ps-3 navbar navbar-dark" @onclick="@(async ()=>await ToggleMenuClick(02))">
<div class="container-fluid">
<a class="navbar-brand" href="javascript:void(0);">任務管理</a>
<button title="Navigation menu" class="navbar-toggler">
<span class="navbar-toggler-icon"></span>
</button>
<div class="@ArrowClass(01)"></div>
</div>
</div>
<!-- 菜單組內容 -->
<div class="@MenuGroupContentClass(02)">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="kpTask/taskList">
<span class="oi oi-list" aria-hidden="true"></span> 任務列表
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="kpTask/taskRunList">
<span class="oi oi-list" aria-hidden="true"></span> 任務執行記錄
</NavLink>
<!-- 二級菜單組 -->
<NavLink class="nav-link" href="javascript:void(0);" @onclick="@(()=> ToggleMenuClick(0201))" style="display:none;">
<span class="oi oi-list" aria-hidden="true"></span> 測試二級菜單組
<div class="@ArrowClass(0201)"></div>
</NavLink>
<!-- 二級菜單組內容 -->
<div class="@MenuGroupContentClass(0201)" style="display:none;">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-list" aria-hidden="true"></span> 測試二級菜單
</NavLink>
</div>
</nav>
</div>
</div>
</nav>
</div>
<!-- 菜單組 -->
<div class="top-row ps-3 navbar navbar-dark" @onclick="@(async ()=>await ToggleMenuClick(03))">
<div class="container-fluid">
<a class="navbar-brand" href="javascript:void(0);">任務管理</a>
<button title="Navigation menu" class="navbar-toggler">
<span class="navbar-toggler-icon"></span>
</button>
<div class="@ArrowClass(01)"></div>
</div>
</div>
<!-- 菜單組內容 -->
<div class="@MenuGroupContentClass(03)">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="kpTask/taskList">
<span class="oi oi-list" aria-hidden="true"></span> 任務列表
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="kpTask/taskRunList">
<span class="oi oi-list" aria-hidden="true"></span> 任務執行記錄
</NavLink>
<!-- 二級菜單組 -->
<NavLink class="nav-link" href="javascript:void(0);" @onclick="@(()=> ToggleMenuClick(0301))" style="display:none;">
<span class="oi oi-list" aria-hidden="true"></span> 測試二級菜單組
<div class="@ArrowClass(0301)"></div>
</NavLink>
<!-- 二級菜單組內容 -->
<div class="@MenuGroupContentClass(0301)" style="display:none;">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-list" aria-hidden="true"></span> 測試二級菜單
</NavLink>
</div>
</nav>
</div>
</div>
</nav>
</div>
@code {
int _index = 01;
Dictionary<int, bool> _dict = new Dictionary<int, bool>();
IJSObjectReference _module;
protected override async void OnAfterRender(bool firstRender)
{
if (firstRender)
{
_module = await _js.InvokeAsync<IJSObjectReference>("import", "./Shared/NavMenu.razor.js");
}
}
private async Task ToggleMenuClick(int index)
{
object[] args = new object[] { index };
object[] objs = await _module.InvokeAsync<object[]>("animate", args);
int time = ((JsonElement)objs[0]).GetInt32();
if (time > 0) { await Task.Delay(time); }
if (index > 99)
{
if (_dict.ContainsKey(index))
{
_dict[index] = !_dict[index];
}
else
{
_dict[index] = false;
}
}
else
{
_index = index;
}
}
private string MenuGroupContentClass(int index)
{
if (index > 99)
{
if (!_dict.ContainsKey(index) || _dict[index])
{
return $"collapse content{index}";
}
else
{
return $"content{index}";
}
}
else
{
if (index == _index)
{
return $"content{index}";
}
else
{
return $"collapse content{index}";
}
}
}
private string ArrowClass(int index)
{
if (index > 99)
{
if (!_dict.ContainsKey(index) || _dict[index])
{
return "arrow-collapse arrow-float";
}
else
{
return "arrow arrow-float";
}
}
else
{
if (index == _index)
{
return "arrow";
}
else
{
return "arrow-collapse";
}
}
}
}