#showdev使用Blazor创建DEV&#039的离线页面

我在创建DEV的离线页面上发现了Ali Spittel的一篇有趣的帖子:

aspittel图像

如何在DEV的离线页面上创建绘图交互

Ali Spittel·7月3日·4分钟阅读

#javascript #艺术 #showdev

鉴于我在过去使用WebAssembly做了一些实验,我决定在WebAssembly中对自己的实现进行一次破解,特别是对Blazor。

入门

警告:Blazor是一个使用.NET堆栈,特别是C#语言构建客户端Web应用程序的平台。它是高度实验性的,所以有可能事情会在写作时发生变化(我正在使用构建 3.0.0-preview6.19307.2)。

首先,您需要按照Blazor的设置指南进行操作,一旦完成,您可以在您喜欢的编辑器中创建一个新项目(我使用的是VS Code)。

然后我删除了所有的样板代码 PagesShared 文件夹(除了任何 _Imports.razor 文件),Bootstrap来自 css 文件夹和 sample-data。现在我们有一个完全空的Blazor项目。

创建我们的布局

我们需要做的第一件事是创建布局文件。与ASP.NET MVC一样,Blazor使用布局文件作为所有页面的基本模板(嗯,所有使用该布局的页面,您可以有多个布局)。所以,在中创建一个新文件 SharedMainLayout.razor 我们将定义它。鉴于我们希望它是全屏,它将非常简单:

@inherits LayoutComponentBase  @Body 

该文件继承了Blazor为布局提供的基类, LayoutComponentBase 这让我们可以访问 @Body 允许我们将页面内容放在我们想要的任何HTML中的属性。我们周围不需要任何东西,所以我们就这么说 @Body 在页面中。

创建我们的离线页面

是时候制作离线页了,我们先从创建一个新文件开始 Pages 文件夹,我们来称呼它 Offline.html

@page "/"  

Offline

这是我们的出发点,首先我们有 @page 该指令告诉Blazor这是一个我们可以导航到的页面,它将响应的URL是 "/"。我们在那里有一些占位符HTML,接下来我们将替换它们。

启动画布

离线页面本质上是一个我们可以绘制的大画布,我们需要创建它,让我们更新 Offline.razor 使用canvas元素:

@page "/"   

设置画布大小

我们需要将画布的大小设置为全屏,现在就是 0x0,不理想。理想情况下,我们希望获得 innerWidthinnerHeight 浏览器,要做到这一点,我们需要使用Blazor的JavaScript互操作。

我们将快速创建一个新的JavaScript文件来交互(调用它 helper.js 并把它放进去 wwwroot,也更新 index.htmlwwwroot 参考它):

window.getWindowSize = () => {     return { height: window.innerHeight, width: window.innerWidth }; }; 

接下来我们将创建一个C# struct 表示该数据(我添加了一个名为的文件 WindowSize.cs 进入项目根目录):

namespace Blazor.DevToOffline {     public struct WindowSize     {         public long Height { get; set; }         public long Width { get; set; }     } }  

最后,我们需要在Blazor组件中使用它:

@page "/" @inject IJSRuntime JsRuntime   height="@windowSize.Height" width="@windowSize.Width">  @code {     WindowSize windowSize;      protected override async Task OnInitAsync()     {         windowSize = await JsRuntime.InvokeAsync("getWindowSize");     } } 

这是一些代码添加,所以让我们分解它。

@inject IJSRuntime JsRuntime 

这里我们使用依赖注入来注入 IJSRuntime 作为一个叫做的财产 JsRuntime 在我们的组件上。

 height="@windowSize.Height" width="@windowSize.Width"> 

接下来,我们将设置 heightwidth 的属性 元素到我们的实例的字段值 struct,一个名为的实例 windowSize。请注意 @ 这告诉编译器这是指C#变量,而不是静态字符串。

@code {     WindowSize windowSize;      protected override async Task OnInitAsync()     {         windowSize = await JsRuntime.InvokeAsync<WindowSize>("getWindowSize");     } } 

现在我们已经在代码块中添加了一个代码块。它包含变量 windowSize (它是未初始化的,但它是一个结构,所以它有一个默认值)然后我们覆盖一个生命周期方法, OnInitAsync,我们调用JavaScript来获取窗口大小并将其分配给我们的局部变量。

恭喜你,你现在有一个全屏画布 ?

接线活动

我们可能会出现我们的画布,但它还没有做任何事情,所以让我们通过添加一些事件处理程序来解决这个问题:

@page "/" @inject IJSRuntime JsRuntime   height="@windowSize.Height"         width="@windowSize.Width"         @onmousedown="@StartPaint"         @onmousemove="@Paint"         @onmouseup="@StopPaint"         @onmouseout="@StopPaint" />  @code {     WindowSize windowSize;      protected override async Task OnInitAsync()     {         windowSize = await JsRuntime.InvokeAsync("getWindowSize");     }      private void StartPaint(UIMouseEventArgs e)     {     }      private async Task Paint(UIMouseEventArgs e)     {     }      private void StopPaint(UIMouseEventArgs e)     {     } } 

当您在Blazor中绑定事件时,您需要在事件名称前加上 @, 喜欢 @onmousedown,然后提供它在事件发生时调用的函数的名称,例如, @StartPaint。这些函数的签名是返回a void 要么 Task,取决于它是否异步。函数的参数需要是适当类型的事件参数,映射到DOM等价物(UIMouseEventArgsUIKeyboardEventArgs等)。

注意:如果您将此与JavaScript参考实现进行比较,您会注意到我没有使用 touch 事件。这是因为,在我今天的实验中,Blazor中存在绑定触摸事件的错误。记住,这是预览

获取画布上下文

注意:我将讨论如何设置交互 来自Blazor,但在一个真实的应用程序中,你更可能想要使用BlazorExtensions / Canvas而不是自己动手。

由于我们需要使用画布的2D上下文,因此我们需要访问它。但是,这是一个JavaScript API,我们在C#/ WebAssembly,这将有点有趣。

最终,我们将在JavaScript中使用它并依赖Blazor的JavaScript互操作功能,所以仍然没有逃避编写一些JavaScript

让我们编写一个JavaScript模块来为我们提供一个API来处理:

((window) => {     let canvasContextCache = {};      let getContext = (canvas) => {         if (!canvasContextCache(canvas)) {             canvasContextCache(canvas) = canvas.getContext('2d');         }         return canvasContextCache(canvas);     };      window.__blazorCanvasInterop = {         drawLine: (canvas, sX, sY, eX, eY) => {             let context = getContext(canvas);              context.lineJoin = 'round';             context.lineWidth = 5;             context.beginPath();             context.moveTo(eX, eY);             context.lineTo(sX, sY);             context.closePath();             context.stroke();         },          setContextPropertyValue: (canvas, propertyName, propertyValue) => {             let context = getContext(canvas);              context(propertyName) = propertyValue;         }     }; })(window); 

我用一个匿名自执行函数创建的闭包范围完成了这个 canvasContextCache我用它来避免经常得到上下文,不会暴露。

该模块为我们提供了两个函数,第一个是在两个点之间在画布上绘制一条线(我们需要用于涂鸦),第二个更新上下文的属性(我们需要更改颜色 )。

您可能还会注意到我从未打过电话 document.getElementById,我只是以某种方式“神奇地”得到画布。这可以通过在C#中捕获组件引用并传递该引用来实现。

但这仍然是所有JavaScript,我们在C#中做什么?好吧,我们创建一个C#盘点类

public class Canvas2DContext {     private readonly IJSRuntime jsRuntime;     private readonly ElementRef canvasRef;      public Canvas2DContext(IJSRuntime jsRuntime, ElementRef canvasRef)     {         this.jsRuntime = jsRuntime;         this.canvasRef = canvasRef;     }      public async Task DrawLine(long startX, long startY, long endX, long endY)     {         await jsRuntime.InvokeAsync<object>("__blazorCanvasInterop.drawLine", canvasRef, startX, startY, endX, endY);     }      public async Task SetStrokeStyleAsync(string strokeStyle)     {         await jsRuntime.InvokeAsync<object>("__blazorCanvasInterop.setContextPropertyValue", canvasRef, "strokeStyle", strokeStyle);     } } 

这是一个通用类,它采用捕获的引用和JavaScript互操作API,并为我们提供了更好的编程接口。

接线我们的背景

我们现在可以连接上下文并准备在画布上绘制线条:

@page "/" @inject IJSRuntime JsRuntime   height="@windowSize.Height"         width="@windowSize.Width"         @onmousedown="@StartPaint"         @onmousemove="@Paint"         @onmouseup="@StopPaint"         @onmouseout="@StopPaint"         @ref="@canvas" />  @code {     ElementRef canvas;      WindowSize windowSize;      Canvas2DContext ctx;     protected override async Task OnInitAsync()     {         windowSize = await JsRuntime.InvokeAsync("getWindowSize");         ctx = new Canvas2DContext(JsRuntime, canvas);     }      private void StartPaint(UIMouseEventArgs e)     {     }      private async Task Paint(UIMouseEventArgs e)     {     }      private void StopPaint(UIMouseEventArgs e)     {     } } 

通过增加 @ref="@canvas" 我们的 元素我们创建我们需要的引用然后在 OnInitAsync 我们创造的功能 Canvas2DContext 我们将使用。

画在画布上

我们终于准备在画布上绘制一些图形,这意味着我们需要实现这些事件处理程序:

    bool isPainting = false;     long x;     long y;     private void StartPaint(UIMouseEventArgs e)     {         x = e.ClientX;         y = e.ClientY;         isPainting = true;     }      private async Task Paint(UIMouseEventArgs e)     {         if (isPainting)         {             var eX = e.ClientX;             var eY = e.ClientY;              await ctx.DrawLine(x, y, eX, eY);             x = eX;             y = eY;         }     }      private void StopPaint(UIMouseEventArgs e)     {         isPainting = false;     } 

不可否认,这些与JavaScript实现没有什么不同,他们所要做的就是从鼠标事件中获取坐标,然后将它们传递给画布上下文盘点器,后者又调用适当的JavaScript函数。

结论

?我们完成了你可以看到它在这里运行,代码在GitHub上。

GitHub标志 aaronpowell / blazor-devto-offline

演示如何使用Blazor创建DEV.to的离线页面

此存储库包含如何使用Blazor创建DEV.to脱机页面的示例。

你可以在这里找到它运行https://blazordevtooffline.z23.web.core.windows.net/。

在GitHub上查看

这是Blazor的快速浏览,但更重要的是,我们如何在可能需要我们与许多场景需要的JavaScript进行更多互操作的场景中使用Blazor。

我希望你喜欢它,并准备好解决你自己的Blazor实验

奖金,颜色选择器

在上面的例子中我们没有做过一件事,实现颜色选择器

我想这样做作为通用组件,所以我们可以这样做:

 OnClick="@SetStrokeColour"               Colours="@colours" /> 

在一个名为的新文件中 ColourPicker.razor (文件名很重要,因为这是组件的名称)我们将创建我们的组件:

 class="colours">     @foreach (var colour in Colours)     {          class="colour"                 @onclick="@OnClick(colour)"                 @key="@colour">              } 
@code { (Parameter) public Func, Action<UIMouseEventArgs>> OnClick { get; set; } (Parameter) public IEnumerable Colours { get; set; } }

我们的组件将有2个参数,可以从父级,颜色集合和单击按钮时调用的函数进行设置。对于我所做的事件处理程序是这样的,你传入一个返回一个动作的函数,所以它是一个单独的函数,它被绑定到颜色的名称,当

你可能还喜欢