添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
风度翩翩的钢笔  ·  encodeURI() - ...·  1 月前    · 
耍酷的咖啡豆  ·  Centos7/8 安装Nginx ...·  1 年前    · 
开朗的松鼠  ·  HaUniqueMergeTree--Byt ...·  1 年前    · 
追风的豆腐  ·  c++ errorC2446 ...·  1 年前    · 
+关注继续查看

1. 背景

电子木鱼不知道从什么时候火了起来,成了年轻人的新时尚。年轻人没有选择经常去寺庙像和尚那样念经,而是下载了电子木鱼软件,进行线上敲木鱼。虽然搞不懂这种“敲电子木鱼,见机甲佛祖,积赛博功德,修图灵真经”的赛博玄学,但是这个软件它做起来容易,需求还简单。在这么焦虑的假期生活中,还是假期的最后一天,最适合玩一玩 .NET MAUI 了。

烦恼-1

2. 相关知识点

这个项目及文章使用并介绍了以下相关知识点:

  1. Blazor 播放音频,主要为 JS 调用
  2. Blazor 调用 .NET MAUI 各平台原生对话框
  3. .NET MAUI 的 Preferences 数据存储
  4. Blazor 循环渲染中的一个常见 BUG

界面 UI 方面因为使用还是 Web 的技术,所以更多的是前端的知识,可自行探索。

3. 开发过程

3.1 需求分析

这个电子木鱼整体功能非常简单,主要就是点击一下屏幕播放一下木鱼敲击的音频,这个是核心功能。

再往下设计,就是一些动效的优化,比如:

  1. 木鱼敲击的动效,放大
  2. 木鱼敲击的震动反馈,当然这一条可能没必要
  3. 木鱼敲击后的文字浮动,一敲“+999功德”这种的

更进一层的设计:

  1. 这里需要引入设置功能了,加入设置菜单
  2. 文字浮动内容的自定义
  3. 自动敲击积累功德
  4. 自定义的敲击音和木鱼样式,这里就要开始费美工了

再接下来迭代,当然也是有很多参考的相关案例,比如:

  1. 排行榜功能,区域排行,日,周,月各种排行榜单
  2. 分享功能,满足人们的分享欲
  3. 添加诵经背景音,使氛围更融洽

当然,以上只是瞎扯,作为一个清心寡欲的木鱼,做好自己定位是第一步。“天之道,不争而善胜”,作为佛系木鱼,当然只是纯粹的敲击,满足最基本的需求就可以了。

3.2 开发第一步

这个开发第一步,沿用我之前讲的那个 《MAUI 安卓 UI 资源设置》 文章的内容,先把 UI 资源处理一下。这次被老婆拒绝了,我亲自拿钢笔工具把这个木鱼 Logo 给绘制出来了,作为一个合格的全栈,美工也是需要会的。

logo

这个 Logo 画完,内部应用的主体界面木鱼自然也就设计好了。

3.3 核心功能

电子木鱼的核心功能,当然是点击屏幕播放一下敲击音效了。因为我们使用的是 .NET MAUI Blazor ,为了后续界面动效的设计,我们通过前端的方式来播放音乐。

为了图方便,直接在 wwwroot/index.html HTML 入口文件中添加如下 audio 标签和 JS 方法:

<audio id="player">
    <source  src="other/muyu.wav" />
</audio>
<script>
window.PlayAudioFile = (tips) => {
    var audio = document.getElementById('player');
    audio.play();
</script>

在应用的首页添加图片点击的事件,来调用 JS,并预留了文字浮动内容的传递:

<img id="img" src="/images/muyu.svg" style="height:100%;max-width:100%" @onclick="ClickHandler" />
async Task ClickHandler()
    await JS.InvokeAsync<string>("PlayAudioFile", tipsInfo);
}

本小节相关知识点: 《JavaScript 互操作》

3.4 动效设计

动效设计这里我们主要实现木鱼敲击放大动效和敲击后的文字浮动。这里主要是前端的技术,我们使用 CSS3 的 @keyframes 创建两个动画规则:

@keyframes showbig {
        transform: scale(1);
    50% {
        transform: scale(1.05);
@keyframes showtips {
        opacity: 1;
        transform: translateY(0);
    75% {
        opacity: .9;
        transform: translateY(-1.5em);
    100% {
        opacity: 0;
        transform: translateY(-2em);
}

这里两个动效都是默认是只循环一遍,时长 0.3s :

.showbig {
    animation-timing-function: ease-in-out;
    animation-duration: .3s;
    animation-name: showbig;
}

通过 JS 方式控制 DOM 的 Class 添加删除来实现动画的播放:

function ShowBig() {
    var img = document.getElementById('img');
    img.classList.add('showbig');
    setTimeout(() => {
        img.classList.remove('showbig');
    }, 300);
}

对于浮动的文字来说,快速敲击时,为了可以显示多条,这边的浮动文字是动态创建的 P 标签。还记得前面预留的 tipsInfo 吧,是在这里使用的。

function ShowTips(text) {
    var p = document.createElement("p");
    p.classList.add('tips');
    p.innerHTML = text;
    var tips = document.getElementById('tips');
    tips.appendChild(p);
    setTimeout(() => {
        tips.removeChild(p)
    }, 300);
}

3.5 菜单设计

有了设置当然就可以搞个菜单出来了,菜单这里的设计也非常简单,我们可以直接新建一个 Blazor 组件 Shared/SettingMenu.razor ,来实现传递菜单项来生成菜单组件,并在点击选项后返回点击的菜单序号。

<div class="mask" style="display:@(Show?"flex":"none")">
    <div class="setting_menu">
        <div class="title">@MenuTitle</div>
        <ul class="list">
            @for (int i=0;i<NavData.Count(); i++)
                <li @onclick="e=>MenuClick(i)">@NavData[i]</li>
</div>
/// <summary>
/// 点击回调
/// </summary>
[Parameter]
public EventCallback<int> OnMenuClick { get; set; }
/// <summary>
/// 菜单标题
/// </summary>
[Parameter]
public string MenuTitle { get; set; } = "设置";
/// <summary>
/// 显示隐藏
/// </summary>
[Parameter]
public bool Show { get; set; } = false;
/// <summary>
/// 菜单信息
/// </summary>
[Parameter]
public string[] NavData { get; set; }
/// <summary>
/// 通知点击事件
/// </summary>
/// <param name="inx">点击的index</param>
/// <returns></returns>
async Task MenuClick(int inx)
    await OnMenuClick.InvokeAsync(inx);
}

那,跑一下看看,似乎,大概,也许是非常完美:

菜单

3.6 对话框

在我们的悬浮文字设置选项中,需要提供输入功能。当然,直接前端的方案,设计一个模态对话框似乎是一个非常好的方案,也符合 UI 的一致性。但是考虑到能省则省,这边没有使用前端的框架,所以再手写一个会稍微费点事的。如果说省事,当然是直接前端 JS 的 prompt ,但是这样又显得格格不入:

prompt

.NET MAUI Blazor 这种混合开发的模式大多都一样,对话框也是一个小问题。不过这里可以非常方便的使用本机对话框,只要拿到 Page 即可,在 razor 文件的 code 代码块中,我们可以这样用:

string result = await Application.Current.MainPage.DisplayPromptAsync("悬浮文字设置", "请设置敲击后的自定义悬浮文字", "确定", "取消", "请输入悬浮文字", 5, null, tipsInfo);

本小节相关知识点: 《显示弹出窗口》

3.7 数据存储

搞定了设置文字的交互,接下来就是存储了,总体来说我们需要存储的木鱼的总敲击次数,今日敲击次数和刚刚的自定义悬浮文字。毕竟不是复杂的应用,就没必要上数据库了,对于这种简单的键值数据存储我们可以直接选用 Preferences Preferences 通过调用 Preferences.Set 方法来设置,提供键和值,以下就是完整的设置悬浮文字和存储的方法:

/// <summary>
/// 获取初始的悬浮文字
/// </summary>
private string tipsInfo = Preferences.Default.Get("tips", "功德+1");
async Task SetTips()
    string result = await Application.Current.MainPage.DisplayPromptAsync("悬浮文字设置", "请设置敲击后的自定义悬浮文字", "确定", "取消", "请输入悬浮文字", 5, null, tipsInfo);
    if (string.IsNullOrWhiteSpace(result)) return;
    Preferences.Default.Set("tips", result);
    tipsInfo = result;
}

本小节相关知识点: 《Preferences》

3.8 Blazor 循环小 BUG

做的差不多了,菜单也有了,可以简单跑一下看看了,但是似乎菜单出了些问题,设置菜单的按钮并不好用,每一个回调都是 4:

BUG

啊对,前面的完美,我是说界面。BUG 总会有的,还总是发生在不经意之间。

元芳你怎么看?

元芳:问一问 ChatGPT 吧,他说他懂这个的。

是的,我了解Blazor。Blazor是一种用于开发Web应用程序的框架,允许使用C#代码和.NET运行时在浏览器中运行Web应用程序。它提供了一种方便的方法来使用.NET技术来构建客户端Web应用程序,而无需学习JavaScript。

仔细检查我们会发现,在前面的 SettingMenu.razor 菜单组件中,我们有一个循环,通过枚举 NavData 数组中的每一项并创建一个包含该项的列表项。因为需要序号,所以这里用的 for 循环,问题在于,在回调函数 MenuClick(i) 中,变量 i 是局部的,在循环结束后其值就改变了。

为了解决这个问题,可以将当前项的索引存储在闭包中:

<ul class="list">
    @for (int i = 0; i < NavData.Count(); i++)
        int index = i;
        <li @onclick="e => MenuClick(index)">@NavData[i]</li>
</ul>

修改完代码,再次运行:

修复后

4. 最后

至此,这个简单的电子木鱼的基础功能已实现,应用已经完成了大半。对于未尽的事宜,比如敲击计数和其他设置的功能我下次再说,容我先去敲一会木鱼,静个心。

静心

.NET MAUI 开发电子木鱼(下)
本文介绍如何使用 .NET MAUI 开发一个电子木鱼应用。以实际的小应用开发为例,通过这个开发过程,介绍了其涉及的 .NET MAUI、Blazor、前端等相关知识点。文章涉及的应用已开源在 Github,大家可前往下载体验: https://github.com/sangyuxiaowu/MuYu
基于.net core 开发的轻量级配置中心 - AgileConfig
AgileConfig 是一个基于 .net core 开发的轻量级配置中心,更多的是为了那些分布式、容器化部署的应用能够更加简单的读取、修改配置。秉承轻量化的,部署简单、配置简单、使用简单、学习简单,它只提取了必要的一些功能,并没有像 Apollo 那样复杂且庞大。但是它的功能也已经足够你替换...
使用.NET开发搭建OpenAI模型的中间服务端
前不久微信上大家玩ChatGPT聊天机器人玩的不亦乐乎;不过随着ChatGPT被封杀,所以用微信聊天机器人有可能导致封号的风险。那如果自己不想每次都去OpenAI官网上进行对话【PS:官网上面聊天对话有局限性,例如回复的内容比较长,AI回答是一个一个字写的,就可能导致超过一定时间以后,变成请求超时之类的异常;而通过API直接访问,可以避免这个情况发生】,想要自己搭建一个服务来本地调用,是不是也可以?
【番外篇】Rust环境搭建+基础开发入门+Rust与.NET6、C++的基础运算性能比较
突然想打算把Rust作为将来自己主要的副编程语言。当然,主语言还是C#,毕竟.NET平台这么强大,写起来就是爽。缘起:之前打算一些新的产品或者新的要开发的东西,由于没有历史包袱,就想重新选型一下,在.NET平台(C#语言)、Golang、Rust里面进行选择一个。
【.NET6+Avalonia】开发支持跨平台的仿WPF应用程序以及基于ubuntu系统的演示
随着跨平台越来越流行,.net core支持跨平台至今也有好几年的光景了。但是目前基于.net的跨平台,大多数还是在使用B/S架构的跨平台上;至于C/S架构,大部分人可能会选择QT进行开发,或者很早之前还有一款Mono可以支持.NET开发者进行开发跨平台应用。
【Kafka】基于Windows环境的Kafka有关环境(scala+zookeeper+kafka+可视化工具)搭建、以及使用.NET环境开发的案例代码与演示
基于Windows系统下的Kafka环境搭建;以及使用.NET 6环境进行开发简单的生产者与消费者的演示。
【.NET 6】使用.NET 6开发minimal api以及依赖注入的实现、VS2022热重载和自动反编译功能的演示
.net 6 LTS版本发布已经有若干天了。此处做一个关于使用.net 6 开发精简版webapi(minimal api)的入门教程,以及VS2022 上面的两个强大的新技能(热重载、代码自动反编译)的顺带演示。
十九、.net core使用SoapCore开发webservice接口,以及使用HttpClientFactory动态访问webservice接口
使用SoapCore实现在.net core平台下开发webservice;以及使用HttpClientFactory动态访问webservice。首先,需要在包项目下面引用SoapCore:
快速入门 .NET nanoFramework 开发 ESP32-Pico 应用
本文是一篇适合初学者的 .NET nanoFramework 保姆级入门教程,并提供了基本的入门程序并介绍了微雪的 ESP32-S2-Pico 使用 .NET nanoFramework 开发过程的基础知识。
推荐基于.Net6+Furion +iView开发的一套极简的进销存管理系统
一个基于.Net Core构建的简单、跨平台、模块化的小程序考试系统。前端采用Vue+iView框架,后端采用Furion架构,让开发变得更简单。
一个基于.Net+Vue开发的开源权限工作流系统
一个基于.Net 5开发的权限管理、工作流系统框架。借鉴了Martin Fowler企业级应用开发思想,框架选项都是使用最新的技术、前后端分离。