【Blazor】在ASP.NET Core中使用Blazor组件 - 创建一个音乐播放器

时间:2023-11-10 08:18:32

前言

Blazor正式版的发布已经有一段时间了,.NET社区的各路高手也创建了一个又一个的Blazor组件库,其中就包括了我和其他小伙伴一起参与的AntDesign组件库,于上周终于发布了第一个版本0.1.0,共计完成了59个常用组件,那么今天就来聊一聊如何在ASP.NET Core MVC项目中使用这些Blazor组件吧

环境搭建

.NET Core SDK 3.0.301

Vistual Studio 2019.16.6.3

调用Blazor组件

创建ASP.NET Core MVC项目,如果想要在已有的项目上使用AntDesign,需要确保Target Framework是netcoreapp3.1,然后在Nuget中搜索并安装AntDesign 0.1.0版本。

修改Startup.cs

在ConfigureServices方法中添加

 // add for balzor
services.AddServerSideBlazor();
// add for AntDesign
services.AddAntDesign();

在Configure方法中添加

 app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
// add for blazor
endpoints.MapBlazorHub();
});

修改./Views/Shared/_Layout.cshtml

在head区域添加

 <!--add for AntDesign-->
<link href="/_content/AntDesign/css/ant-design-blazor.css" rel="stylesheet">
<base href="/" />

在script区域添加

 <!--add for blazor-->
<script src="~/_framework/blazor.server.js"></script>

这里我们需要利用一个中间层,否则直接在View里添加组件会有很多限制,不太方便

创建一个razor文件./Components/HelloWorld.razor

 @using AntDesign

 <Button type="primary" OnClick="(e)=>OnClick(e)">@_content</Button>

 @code{
private string _content = "Primay";
private void OnClick(Microsoft.AspNetCore.Components.Web.MouseEventArgs args)
{
_content += "*";
}
}

最后在View中添加刚刚新建的中间组件

修改./Views/Home/Index.cshtml,添加代码

 <component type="typeof(HelloWorld)" render-mode="ServerPrerendered" />

Build & Run

这时候主页应该会出现一个ant-design风格的button,点击后button的内容会变为Priamary*,每点击一次就多一个*,效果如下

 

【Blazor】在ASP.NET Core中使用Blazor组件 - 创建一个音乐播放器

小结

一般来说,在MVC项目中,先将界面需要使用的组件组合在一起,然后整体包装在一个中间组件(HelloWolrd.razor)中,最后在调用的View中展示中间组件。可以理解为组件库为我们提供了各种各样的零件,中间层将这些零件(以及原生HTML标签)组合成一个产品,最后在View中展示产品。

创建一个播放器组件

首先我们创建好需要用到的JavaScript脚本

Nuget安装Microsoft.TypeScript.MSBuild

创建文件main.ts

 interface Window {
SoBrian: any;
} function Play(element, flag) {
var dom = document.querySelector(element);
if (flag) {
dom.play();
}
else {
dom.pause();
}
} function GetMusicTime(element) {
var dom = document.querySelector(element);
let obj = {
currentTime: dom.currentTime,
duration: dom.duration
}
let json = JSON.stringify(obj); return json
} function SetMusicTime(element, time) {
var dom = document.querySelector(element);
dom.currentTime = time;
} window.Music = {
print: Print,
play: Play,
getMusicTime: GetMusicTime,
setMusicTime: SetMusicTime
}

创建文件tsconfig.json

{
"compileOnSave": true,
"compilerOptions": {
"noImplicitAny": false,
"noEmitOnError": true,
"removeComments": false,
"sourceMap": false,
"target": "es2015",
"outDir": "wwwroot/js"
},
"files": [ "main.ts" ],
"exclude": [
"node_modules",
"wwwroot"
]
}

创建文件夹./wwwroot/music/

放入几首你喜欢的音乐,但要注意支持的文件格式

<audio> can be used to play sound files in the following formats:

.mp3: Supported by all modern browsers.
.wav: Not supported by Internet Explorer.
.ogg: Not supported by Internet Explorer and Safari.
 

创建./Components/MusicPlayer.razor

 @namespace SoBrian.MVC.Components
@inherits AntDesign.AntDomComponentBase <audio id="audio" preload="auto" src="@_currentSrc"></audio>
<div>
<AntDesign.Row Justify="center" Align="middle">
<AntDesign.Col Span="4">
<p>@System.IO.Path.GetFileNameWithoutExtension(_currentSrc)</p>
</AntDesign.Col>
<AntDesign.Col Span="4">
<AntDesign.Space>
<AntDesign.SpaceItem>
<AntDesign.Button Type="primary" Shape="circle" Icon="left" OnClick="OnLast" />
</AntDesign.SpaceItem>
<AntDesign.SpaceItem>
<AntDesign.Button Type="primary" Shape="circle" Icon="@PlayPauseIcon" Size="large" OnClick="OnPlayPause" />
</AntDesign.SpaceItem>
<AntDesign.SpaceItem>
<AntDesign.Button Type="primary" Shape="circle" Icon="right" OnClick="OnNext" />
</AntDesign.SpaceItem>
</AntDesign.Space>
</AntDesign.Col>
<AntDesign.Col Span="9">
<AntDesign.Slider Value="@_currentTimeSlide" OnAfterChange="OnSliderChange" />
</AntDesign.Col>
<AntDesign.Col Span="3">
<p>@($"{_currentTime.ToString("mm\\:ss")} / {_duration.ToString("mm\\:ss")}")</p>
</AntDesign.Col>
</AntDesign.Row>
</div>

创建./Components/MusicPlayer.razor.cs

 public partial class MusicPlayer : AntDomComponentBase
{
private bool _isPlaying = false;
private bool _canPlayFlag = false;
private string _currentSrc;
private List<string> _musicList = new List<string>
{
"music/周杰伦 - 兰亭序.mp3",
"music/周杰伦 - 告白气球.mp3",
"music/周杰伦 - 听妈妈的话.mp3",
"music/周杰伦 - 园游会.mp3",
"music/周杰伦 - 夜曲.mp3",
"music/周杰伦 - 夜的第七章.mp3",
"music/周杰伦 - 搁浅.mp3"
};
private Timer _timer;
private double _currentTimeSlide = ;
private TimeSpan _currentTime = new TimeSpan();
private TimeSpan _duration = new TimeSpan();
private string PlayPauseIcon { get => _isPlaying ? "pause" : "caret-right"; }
private Action _afterCanPlay;
[Inject]
private DomEventService DomEventService { get; set; } protected override void OnInitialized()
{
base.OnInitialized(); _currentSrc = _musicList[];
_afterCanPlay = async () =>
{
// do not use _isPlaying, this delegate will be triggered when user clicked play button
if (_canPlayFlag)
{
try
{
await JsInvokeAsync("Music.play", "#audio", true);
_canPlayFlag = false;
}
catch (Exception ex)
{
}
}
};
} protected override Task OnFirstAfterRenderAsync()
{
// cannot listen to dom events in OnInitialized while render-mode is ServerPrerendered
DomEventService.AddEventListener<JsonElement>("#audio", "timeupdate", OnTimeUpdate);
DomEventService.AddEventListener<JsonElement>("#audio", "canplay", OnCanPlay);
DomEventService.AddEventListener<JsonElement>("#audio", "play", OnPlay);
DomEventService.AddEventListener<JsonElement>("#audio", "pause", OnPause);
DomEventService.AddEventListener<JsonElement>("#audio", "ended", OnEnd);
return base.OnFirstAfterRenderAsync();
} #region Audio EventHandlers private async void OnPlayPause(MouseEventArgs args)
{
try
{
await JsInvokeAsync("Music.play", "#audio", !_isPlaying);
}
catch (Exception ex)
{
}
} private async void OnCanPlay(JsonElement jsonElement)
{
try
{
string json = await JsInvokeAsync<string>("Music.getMusicTime", "#audio");
jsonElement = JsonDocument.Parse(json).RootElement;
_duration = TimeSpan.FromSeconds(jsonElement.GetProperty("duration").GetDouble()); _afterCanPlay();
}
catch (Exception)
{
}
} private void OnPlay(JsonElement jsonElement)
{
_isPlaying = true;
} private async void OnLast(MouseEventArgs args)
{
_canPlayFlag = true;
int index = _musicList.IndexOf(_currentSrc);
index = index == ? _musicList.Count - : index - ;
_currentSrc = _musicList[index];
} private async void OnNext(MouseEventArgs args)
{
_canPlayFlag = true;
int index = _musicList.IndexOf(_currentSrc);
index = index == _musicList.Count - ? : index + ;
_currentSrc = _musicList[index];
} private void OnPause(JsonElement jsonElement)
{
_isPlaying = false;
StateHasChanged();
} private void OnEnd(JsonElement jsonElement)
{
_isPlaying = false;
StateHasChanged(); OnNext(new MouseEventArgs());
} private async void OnTimeUpdate(JsonElement jsonElement)
{
// do not use the timestamp from timeupdate event, which is the total time the audio has been working
// use the currentTime property from audio element
string json = await JsInvokeAsync<string>("Music.getMusicTime", "#audio");
jsonElement = JsonDocument.Parse(json).RootElement;
_currentTime = TimeSpan.FromSeconds(jsonElement.GetProperty("currentTime").GetDouble());
_currentTimeSlide = _currentTime / _duration * ; StateHasChanged();
} #endregion private async void OnSliderChange(OneOf<double, (double, double)> value)
{
_currentTime = value.AsT0 * _duration / ;
_currentTimeSlide = _currentTime / _duration * ;
await JsInvokeAsync("Music.setMusicTime", "#audio", _currentTime.TotalSeconds);
}
}

创建./Controllers/MusicController.cs

 public class MusicController : Controller
{
public IActionResult Index(string name)
{
return View();
}
}

创建./Views/Music/Index.cshtml

 <component type="typeof(MusicPlayer)" render-mode="Server" />

修改./Views/Shared/_Layout.cshtml,添加以下代码

 <li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Music" asp-action="Index">Music</a>
</li>

Build & Run

点击菜单栏的Music,效果如下

【Blazor】在ASP.NET Core中使用Blazor组件 - 创建一个音乐播放器

总结

【Blazor】在ASP.NET Core中使用Blazor组件 - 创建一个音乐播放器

WebAssembly并不是JavaScript的替代品,Blazor当然也不是,在开发Blazor组件的过程中,大部分情况下,仍然要通过TypeScript / JavaScript来与DOM进行交互,比如在这个播放器的案例中,还是需要JavaScript来调用audio的play,pause等方法。但是在View层面使用播放器这个组件时,我们几乎可以不再关心JavaScript的开发。这让前端的开发更类似于开发WPF的XAML界面。事实上,社区里也有这样的项目,致力于提供一种类WPF界面开发的组件库。

同时,也希望大家能多多关注国内小伙伴们共同参与开发的AntDesign,作为最热门的Blazor组件库之一,在今年的MS Build大会上也获得了微软官方的认可。虽然目前组件还有不少BUG和性能问题,但是在社区的努力下,相信它会越来越好,让我们一起为.NET生态添砖加瓦!

参考:

https://catswhocode.com/html-audio-tag

https://www.w3schools.com/TAGS/tag_audio.asp

https://github.com/ant-design-blazor/ant-design-blazor

【Blazor】在ASP.NET Core中使用Blazor组件 - 创建一个音乐播放器