【Blazor】在ASP.NET Core中使用Blazor组件 - 创建一个音乐播放器
前言
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*,每点击一次就多一个*,效果如下
小结
一般来说,在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,效果如下
总结
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组件 - 创建一个音乐播放器的更多相关文章
- 玩转ASP.NET Core中的日志组件
简介 日志组件,作为程序员使用频率最高的组件,给程序员开发调试程序提供了必要的信息.ASP.NET Core中内置了一个通用日志接口ILogger,并实现了多种内置的日志提供器,例如 Console ...
- Asp.Net Core中利用Seq组件展示结构化日志功能
在一次.Net Core小项目的开发中,掌握的不够深入,对日志记录并没有好好利用,以至于一出现异常问题,都得跑动服务器上查看,那时一度怀疑自己肯定没学好,不然这一块日志不可能需要自己扒服务器日志来查看 ...
- ASP.NET Core中使用表达式树创建URL
当我们在ASP.NET Core中生成一个action的url会这样写: var url=_urlHelper.Action("Index", "Home"); ...
- Redis系列文章总结:ASP.Net Core 中如何借助CSRedis实现一个安全高效的分布式锁
引言:最近回头看了看开发的.Net Core 2.1项目的复盘总结,其中在多处用到Redis实现的分布式锁,虽然在OnResultExecuting方法中做了防止死锁的处理,但在某些场景下还是会发生死 ...
- ASP.NET Core中的依赖注入(3): 服务的注册与提供
在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象.ASP.NET Core ...
- ASP.NET Core中的ActionFilter与DI
一.简介 前几篇文章都是讲ASP.NET Core MVC中的依赖注入(DI)与扩展点的,也许大家都发现在ASP.NET CORE中所有的组件都是通过依赖注入来扩展的,而且面向一组功能就会有一组接口或 ...
- ASP.NET Core中使用GraphQL - 第三章 依赖注入
ASP.NET Core中使用GraphQL ASP.NET Core中使用GraphQL - 第一章 Hello World ASP.NET Core中使用GraphQL - 第二章 中间件 SOL ...
- ASP.NET Core 中 HttpContext 详解与使用 | Microsoft.AspNetCore.Http 详解 (转载)
“传导体” HttpContext 要理解 HttpContext 是干嘛的,首先,看图 图一 内网访问程序 图二 反向代理访问程序 ASP.NET Core 程序中,Kestrel 是一个基于 li ...
- ASP.NET Core 中 HttpContext 详解与使用 | Microsoft.AspNetCore.Http 详解
笔者没有学 ASP.NET,直接学 ASP.NET Core ,学完 ASP.NET Core MVC 基础后,开始学习 ASP.NET Core 的运行原理.发现应用程序有一个非常主要的 “传导体” ...
随机推荐
- @Ajax.ActionLink跳转页面的问题解决方案 MVC Ajax不支持问题
[JavaScriptResult]在客户端执行服务器返回的JavaScript代码当一个内置的Ajax辅助方法请求一个操作方法,该方法会返回一个在客户端执行立即的脚本. public ActionR ...
- void out2() const{
include "stdafx.h" include using namespace std; class aa{ int num; public: aa(){ int b =10 ...
- mysql where与 having的区别
where是针对磁盘的数据文件,having是针对存在内存的结果集的筛选. 例如: select name ,(xxx - xxx) as a from table where a > 10; ...
- 用Springboot干掉IBM的WAS-为公司省点钱
1 那一夜,你伤害了我 今夜的雨下得凉快,小南睡得正香,突然收到远洋运维小周的电话:Hello, Are you OK? WAS有issue,快起来help me! 只见小南登陆WAS机,查看了机器日 ...
- spark源码解析大全
第1章 Spark 整体概述 1.1 整体概念 Apache Spark 是一个开源的通用集群计算系统,它提供了 High-level 编程 API,支持 Scala.Java 和 Pytho ...
- Jlink设置正确,但下载程序失败
[图中reset and run]勾选后即每次·下载程序后会自动复位,不需要再在硬件上进行复位 各参数设置正确 但依然下载失败. 原因是需要重新再编译一次,因为上次设置错误,编译后目标未创建! 重新编 ...
- Android学习笔记BroadcastReceiver(广播接收者)
Android发送广播的过程 代码实现 MainActivity.java import androidx.appcompat.app.AppCompatActivity; import androi ...
- 从字符串到常量池,一文看懂String类设计
从一道面试题开始 看到这个标题,你肯定以为我又要讲这道面试题了 // 这行代码创建了几个对象? String s3 = new String("1"); 是的,没错,我确实要从这里 ...
- 【JMeter_01】JMeter介绍与环境搭建
JMeter介绍 Apache JMeter™应用开源软件,100%纯Java应用程序,设计之初是用于负载功能测试和性能测试.但因它在实现对各种接口的调用方面比较成熟,因此,常被用做接口功能测试. J ...
- Java根据模板生成Word文档
一,首先制作模板 1.先做一个Word文档, 2.打开Word,然后另存为*.xml文件 3.最后修改*.xml文件的后缀名为*.ftl 二,打开项目编辑器Idea,在pom文件中引入相关架包依赖(我 ...