65.9K
CodeProject 正在变化。 阅读更多。
Home

在Blazor服务器端使用HTML Canvas进行绘制

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.48/5 (8投票s)

2020年6月2日

CPOL

5分钟阅读

viewsIcon

50204

HTML Canvas 元素支持绘图。本文介绍如何在 Blazor Server-side 中使用它。

引言

Blazor Server-side 是一项基于 .NET Core 的开源技术,支持 Windows、Linux 和 Apple 操作系统。您可以使用 C# 编写所有代码,这是我选择的语言。您可以使用 .NET Core CLR 支持的任何语言。

HTML <canvas> 元素允许您通过 JavaScript 函数对其进行操作来绘图。

将两者结合起来,可以创建一个 Web 页面,您可以在其中在客户端浏览器上的 canvas 元素上进行绘图。

您可以绘制任何您想要的内容。要了解在 JavaScript 中 <canvas> 元素上可以绘制的所有内容,请参阅以下文章:

GitHub 存储库位于:

您根据需要使用的本系列中的其他文章已发布:

要求

安装 Visual Studio 2019 社区版,如果您或您的雇主提供,也可以使用专业版或企业版。在安装 VS 2019 时,您需要勾选 Web 开发部分,以便获得 Blazor Server-side 解决方案模板。

您需要了解基础的 C# 知识。

创建默认的 Blazor Server-Side 解决方案(面向初学者)

打开 Visual Studio 2019,然后单击 创建新项目 按钮。

然后在搜索框中输入 Blazor。您应该会看到 Blazor 应用模板。确保它是 C# 的。然后选择它,并单击右下角的 下一步 按钮。

然后为项目命名。我选择将我的项目命名为 BlazorCanvasApp。为解决方案文件指定您希望将其放置在驱动器上的位置。如果您愿意,可以保留默认位置。

请不要像示例那样输入位置,因为这是我自己的驱动器文件夹路径,在您的计算机上不存在。

然后单击右下角的 创建 按钮。

然后选择 Blazor Server App。这将创建一个 Blazor Server-side 应用。单击右下角的 创建 按钮。

然后 Blazor 解决方案将被创建。它包含一些默认代码。所以只需点击 运行 按钮,让它进行构建、编译和运行,它将在您的默认浏览器中启动。

我的默认浏览器是 Windows 10 Pro 上的 Google Chrome。结果如下图所示:

现在将 Nuget 包添加到解决方案中(面向初学者)

在 Visual Studio 2019 的菜单中,选择菜单选项 工具 => NuGet 包管理器 => 管理解决方案的 NuGet 包...

这将弹出窗口,我们在其中将 Blazor.Extensions.Canvas NuGet 包添加到我们的项目中。

现在,确保您处于 浏览 选项卡。然后在搜索框中,输入 Blazor.Extensions.Canvas。单击并选择 Blazor.Extensions.Canvas 来选择 NuGet 包。然后在右侧,单击 复选框 以便为项目选择它。然后单击 安装 按钮。等待 NuGet 包安装完成。

以同样的方式,添加 Newtonsoft.Json NuGet 包。我们将使用它来进行 JavaScript Interop。JSInterop 是一种方法,通过这种方法我们可以从 C# 代码调用 JavaScript 函数。

编写 Canvas 绘图 Razor 组件(面向所有人)

在 Visual Studio 中的右侧窗格中,转到 解决方案资源管理器 窗格。展开 Pages 文件夹节点。右键单击它,然后在弹出菜单中,选择菜单选项 添加 => Razor 组件...

在弹出模态窗口中,为组件命名。我将其命名为 CanvasDrawing.razor。然后单击 添加 按钮。

在此文件中,粘贴以下代码:

@page "/CanvasDrawing"
@using Blazor.Extensions
@using Blazor.Extensions.Canvas
@using Blazor.Extensions.Canvas.Canvas2D
@using Newtonsoft.Json
@using Newtonsoft.Json.Linq 
@inject IJSRuntime jsRuntime

<div @ref="divCanvas" @onclick="OnClick">
    <BECanvas @ref="myCanvas" Height="800" Width="800"></BECanvas>
</div>

<h3>CanvasDrawing</h3>

@code {
    ElementReference divCanvas;
    Blazor.Extensions.BECanvasComponent myCanvas;
    Canvas2DContext currentCanvasContext;

    class PieChartSlice
    {
        public string Name { get; set; }
        public double PercentageOfSlice { get; set; }
        public string ColorOfSlice { get; set; }
        public double LabelX { get; set; }
        public double LabelY { get; set; }
    }

    async void OnClick(MouseEventArgs eventArgs)
    {
        double mouseX = 0;
        double mouseY = 0;
        List<PieChartSlice> pieChartData = new List<PieChartSlice>();

        if (divCanvas.Id?.Length > 0)
        {
            string data = await jsRuntime.InvokeAsync<string>("getDivCanvasOffsets",
                new object[] { divCanvas });
            JObject offsets = (JObject)JsonConvert.DeserializeObject(data);
            mouseX = eventArgs.ClientX - offsets.Value<double>("offsetLeft");
            mouseY = eventArgs.ClientY - offsets.Value<double>("offsetTop");

            currentCanvasContext = await myCanvas.CreateCanvas2DAsync();

            await currentCanvasContext.ClearRectAsync(0, 0, 800, 800);
            await currentCanvasContext.SetFillStyleAsync("Red");
            await currentCanvasContext.FillRectAsync(mouseX, mouseY, 5, 5);
            await currentCanvasContext.StrokeTextAsync("ClientX: " + mouseX +
                "   Client Y: " + mouseY, 20, 20);

            pieChartData.Add(new PieChartSlice { 
                Name = "Akshay's Company", 
                PercentageOfSlice = 50, 
                ColorOfSlice = "Blue", 
                LabelX = 287, 
                LabelY = 459 });
            pieChartData.Add(new PieChartSlice { 
                Name = "John Doe's Company", 
                PercentageOfSlice = 30, 
                ColorOfSlice = "Red", 
                LabelX = 200, 
                LabelY = 260 });
            pieChartData.Add(new PieChartSlice {
                Name = "Jane Doe's Company", 
                PercentageOfSlice = 20, 
                ColorOfSlice = "Green", 
                LabelX = 400, 
                LabelY = 280 });

            if (myCanvas != null && currentCanvasContext != null)
            {
                await currentCanvasContext.SaveAsync();
                await currentCanvasContext.SetFillStyleAsync("Black");
                await currentCanvasContext.SetFontAsync("15pt Ariel");
                await currentCanvasContext.FillTextAsync(
                    "Widget Industry Market Share Breakup %", 160, 120);
                await currentCanvasContext.SetFontAsync("12pt Ariel");
                await currentCanvasContext.SetStrokeStyleAsync("Black");
                double currangle = 0;
                double lastangle = 0;
                for (var i = 0; i < pieChartData.Count; i++)
                {
                    currangle += (pieChartData[i].PercentageOfSlice * 360) / 100;
                    await currentCanvasContext.SetFillStyleAsync(
                        pieChartData[i].ColorOfSlice);
                    await currentCanvasContext.BeginPathAsync();
                    await currentCanvasContext.MoveToAsync(350, 350);
                    await currentCanvasContext.ArcAsync(350, 350, 200, 
                        (Math.PI / 180) * lastangle, 
                        (Math.PI / 180) * currangle, false);
                    await currentCanvasContext.ClosePathAsync();
                    await currentCanvasContext.FillAsync();
                    await currentCanvasContext.StrokeTextAsync(pieChartData[i].Name, 
                        pieChartData[i].LabelX, pieChartData[i].LabelY);
                    lastangle = currangle;
                }
                await currentCanvasContext.RestoreAsync();
            }
        }
    }
}

@page 指令表示 URL 将是基本 URL + /CanvasDrawing@using 与 C# 的 using 语句相同。@inject 指令将 IJSRuntime 注入到 Razor 组件中。这使我们能够进行 JSInterop。我们可以使用类型为 IJSRuntimejsRuntime 变量来调用我们的 JavaScript 函数,该函数将返回 <div>offsetLeftoffsetTop 属性值。然后,这些值将从鼠标单击事件参数中的 ClientXClientY 中减去,以获取鼠标在 <canvas> 元素内的实际位置。<div> 元素中的 @ref 创建一个 ElementReference。这被传递给 JavaScript 函数以获取偏移量。BECanvasBlazor.Extensions.Canvas,它创建一个 <canvas> 元素。@ref 允许我们获取对 <canvas> 元素的引用,以创建 2D 上下文,我们将在 <canvas> 元素上使用该上下文进行绘图。

为什么我用 Div 包裹 Canvas 元素?这是因为在撰写本文时,OnClick 事件不适用于 Canvas 元素。像这样的注意事项有很多,因为 Blazor 是一项新技术。

现在我们需要在 _Host.cshtml 文件中创建 JavaScript 函数 getDivCanvasOffsets。所以打开这个文件,在最后一个 body 结束标签之前,粘贴以下代码:

    <script src="_content/Blazor.Extensions.Canvas/blazor.extensions.canvas.js"></script>
    <script>
        function getDivCanvasOffsets(el) {
            var obj = {};
            obj.offsetLeft = el.offsetLeft;
            obj.offsetTop = el.offsetTop;
            return JSON.stringify(obj);
        }
    </script>

第一个脚本是用于 Blazor.Extensions.NuGet 包的。它包含 NuGet 包用于 JSInterop 来控制 <canvas> 元素的所有 JavaScript 函数。

现在您可以在 Visual Studio 中运行项目,然后您将进入浏览器中的常规页面。您需要更改 URL 以指向 razor 组件的路由,在我的情况下是 localhost:4046/CanvasDrawing。您可以单击画布,它将在您单击的画布左上角处绘制坐标。在您单击的点,它将绘制一个小的 5x5 像素的红色填充矩形。

下面是一个输出示例:

如果您可以绘制一些文本和 红色 矩形,那么您就可以绘制任何内容。

我建议首先学习 JavaScript 中完整的 Canvas API。然后意识到,如果 JavaScript API for Canvas 元素中有 MoveTo(x,y),那么它在 Blazor.Extensions.Canvas 中的对应项是 MoveToAsync(x,y)。当然,所有绘图函数都在 Canvas 2D 上下文中,该上下文是从 Canvas 元素派生的。模式遵循 HTML <canvas> 元素的 JavaScript API。有些东西,比如 Gradients,在 Blazor.Extensions.Canvas 中不可用。您必须注意哪些可用。

历史

  • 2020 年 6 月 2 日 - 初始版本
© . All rights reserved.