Blazor组件自做四 : 使用JS隔离封装signature_pad签名组件

发布一下 0 0

运行截图

演示地址:DemoSSR

响应式:DemoSSR

Blazor组件自做四 : 使用JS隔离封装signature_pad签名组件

感谢szimek写的棒棒的signature_pad.js项目, 来源:https://github.com/szimek/signature_pad

正式开始

1. 在文件夹wwwroot/lib,添加signature_pad子文件夹,里面下载库文件(文件文末源码里可复制) signature_pad.umd.js复制到此文件夹. 最终版本参考如下

+signature_pad  |-signature_pad.umd.js

2. 添加app.js文件

+signature_pad  |-app.js

代码里 wrapperc.invokeMethodAsync("signatureResult", imgBase64) 为签名canvas结果回调到c#

js代码

import '/lib/signature_pad/signature_pad.umd.js';export function init(wrapperc, element, alertText,) {    //Code modify from https://github.com/szimek/signature_pad    var wrapper = element;//document.getElementById("signature-pad");    var clearButton = wrapper.querySelector("[data-action=clear]");    var changeColorButton = wrapper.querySelector("[data-action=change-color]");    var undoButton = wrapper.querySelector("[data-action=undo]");    var saveBase64Button = wrapper.querySelector("[data-action=save-base64]");    var savePNGButton = wrapper.querySelector("[data-action=save-png]");    var saveJPGButton = wrapper.querySelector("[data-action=save-jpg]");    var saveSVGButton = wrapper.querySelector("[data-action=save-svg]");    var canvas = wrapper.querySelector("canvas");    var signaturePad = new SignaturePad(canvas, {        // It's Necessary to use an opaque color when saving image as JPEG;        // this option can be omitted if only saving as PNG or SVG        backgroundColor: 'rgb(255, 255, 255)'    });    // Adjust canvas coordinate space taking into account pixel ratio,    // to make it look crisp on mobile devices.    // This also causes canvas to be cleared.    function resizeCanvas() {        // When zoomed out to less than 100%, for some very strange reason,        // some browsers report devicePixelRatio as less than 1        // and only part of the canvas is cleared then.        var ratio = Math.max(window.devicePixelRatio || 1, 1);        // This part causes the canvas to be cleared        canvas.width = canvas.offsetWidth * ratio;        canvas.height = canvas.offsetHeight * ratio;        canvas.getContext("2d").scale(ratio, ratio);        // This library does not listen for canvas changes, so after the canvas is automatically        // cleared by the browser, SignaturePad#isEmpty might still return false, even though the        // canvas looks empty, because the internal data of this library wasn't cleared. To make sure        // that the state of this library is consistent with visual state of the canvas, you        // have to clear it manually.        signaturePad.clear();    }    // On mobile devices it might make more sense to listen to orientation change,    // rather than window resize events.    window.onresize = resizeCanvas;    resizeCanvas();    function download(dataURL, filename) {        if (navigator.userAgent.indexOf("Safari") > -1 && navigator.userAgent.indexOf("Chrome") === -1) {            window.open(dataURL);        } else {            var blob = dataURLToBlob(dataURL);            var url = window.URL.createObjectURL(blob);            var a = document.createElement("a");            a.style = "display: none";            a.href = url;            a.download = filename;            document.body.appendChild(a);            a.click();            window.URL.revokeObjectURL(url);        }    }    // One could simply use Canvas#toBlob method instead, but it's just to show    // that it can be done using result of SignaturePad#toDataURL.    function dataURLToBlob(dataURL) {        // Code taken from https://github.com/ebidel/filer.js        var parts = dataURL.split(';base64,');        var contentType = parts[0].split(":")[1];        var raw = window.atob(parts[1]);        var rawLength = raw.length;        var uInt8Array = new Uint8Array(rawLength);        for (var i = 0; i < rawLength; ++i) {            uInt8Array[i] = raw.charCodeAt(i);        }        return new Blob([uInt8Array], { type: contentType });    }    if (clearButton) clearButton.addEventListener("click", function (event) {        signaturePad.clear();        return wrapperc.invokeMethodAsync("signatureResult", null);    });    if (undoButton) undoButton.addEventListener("click", function (event) {        var data = signaturePad.toData();        if (data) {            data.pop(); // remove the last dot or line            signaturePad.fromData(data);        }    });    if (changeColorButton) changeColorButton.addEventListener("click", function (event) {        var r = Math.round(Math.random() * 255);        var g = Math.round(Math.random() * 255);        var b = Math.round(Math.random() * 255);        var color = "rgb(" + r + "," + g + "," + b + ")";        signaturePad.penColor = color;    });    if (saveBase64Button) saveBase64Button.addEventListener("click", function (event) {        if (signaturePad.isEmpty()) {            alertMessage();        } else {            var imgBase64 = signaturePad.toDataURL("image/jpeg");            //console.log(imgBase64);            return wrapperc.invokeMethodAsync("signatureResult", imgBase64);        }    });    if (savePNGButton) savePNGButton.addEventListener("click", function (event) {        if (signaturePad.isEmpty()) {            alertMessage();        } else {            var dataURL = signaturePad.toDataURL();            download(dataURL, "signature.png");        }    });    if (saveJPGButton) saveJPGButton.addEventListener("click", function (event) {        if (signaturePad.isEmpty()) {            alertMessage();        } else {            var dataURL = signaturePad.toDataURL("image/jpeg");            download(dataURL, "signature.jpg");        }    });    if (saveSVGButton) saveSVGButton.addEventListener("click", function (event) {        if (signaturePad.isEmpty()) {            alertMessage();        } else {            var dataURL = signaturePad.toDataURL('image/svg+xml');            download(dataURL, "signature.svg");        }    });    function alertMessage() {        if (alertText) alert(alertText);        wrapperc.invokeMethodAsync("signatureAlert");    }}

3. 打开Components文件夹 , 新建SignaturePad.razor.css文件

css代码

*,*::before,*::after {    box-sizing: border-box;}.signature-pad-body {    display: -webkit-box;    display: -ms-flexbox;    display: flex;    -webkit-box-pack: center;    -ms-flex-pack: center;    justify-content: center;    -webkit-box-align: center;    -ms-flex-align: center;    align-items: center;    height: 400px;    width: 100%;    -webkit-user-select: none;    -moz-user-select: none;    -ms-user-select: none;    user-select: none;    margin: 0;    padding: 32px 16px;    font-family: Helvetica, Sans-Serif;}.signature-pad {    position: relative;    display: -webkit-box;    display: -ms-flexbox;    display: flex;    -webkit-box-orient: vertical;    -webkit-box-direction: normal;    -ms-flex-direction: column;    flex-direction: column;    font-size: 10px;    width: 100%;    height: 100%;    max-width: 650px;    max-height: 400px;    border: 1px solid #e8e8e8;    background-color: #fff;    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.27), 0 0 40px rgba(0, 0, 0, 0.08) inset;    border-radius: 4px;    padding: 16px;}    .signature-pad::before,    .signature-pad::after {        position: absolute;        z-index: -1;        content: "";        width: 40%;        height: 10px;        bottom: 10px;        background: transparent;        box-shadow: 0 8px 12px rgba(0, 0, 0, 0.4);    }    .signature-pad::before {        left: 20px;        -webkit-transform: skew(-3deg) rotate(-3deg);        transform: skew(-3deg) rotate(-3deg);    }    .signature-pad::after {        right: 20px;        -webkit-transform: skew(3deg) rotate(3deg);        transform: skew(3deg) rotate(3deg);    }.signature-pad--body {    position: relative;    -webkit-box-flex: 1;    -ms-flex: 1;    flex: 1;    border: 1px solid #f4f4f4;}    .signature-pad--body    canvas {        position: absolute;        left: 0;        top: 0;        width: 100%;        height: 100%;        border-radius: 4px;        box-shadow: 0 0 5px rgba(0, 0, 0, 0.02) inset;    }.signature-pad--footer {    color: #C3C3C3;    text-align: center;    font-size: 1.2em;    margin-top: 8px;}.signature-pad--actions {    display: -webkit-box;    display: -ms-flexbox;    display: flex;    -webkit-box-pack: justify;    -ms-flex-pack: justify;    justify-content: space-between;    margin-top: 8px;}#github img {    border: 0;}@media (max-width: 940px) {    #github img {        width: 90px;        height: 90px;    }}

4. 打开Components文件夹 , 新建SignaturePad.razor组件

参考阅读:Blazor组件参数

4.1 组件参数

在 ASP.NET Web Forms 中,可以使用公共属性将参数和数据传递到控件。 这些属性可以使用特性在标记中进行设置,也可以直接在代码中设置。 Razor 组件以类似的方式工作,尽管组件属性还必须使用 [Parameter] 特性进行标记才能被视为组件参数。

以下 Counter 组件定义名为 IncrementAmount 的组件参数,该参数可用于指定每次单击按钮时 Counter 应该递增的数量。

razor

<h1>Counter</h1><p>Current count: @currentCount</p><button class="btn btn-primary" @onclick="IncrementCount">Click me</button>@code {    int currentCount = 0;    [Parameter]    public int IncrementAmount { get; set; } = 1;    void IncrementCount()    {        currentCount+=IncrementAmount;    }}

若要在 Blazor 中指定组件参数,请像在 ASP.NET Web Forms 中一样使用特性:

razor

<Counter IncrementAmount="10" />

4.2 C#组件参数实例

定义名为 SaveBase64BtnTitle 的组件参数,该参数可用于设置或者获取 [保存为base64]按钮的文本。

定义名为 OnResult 的组件参数,该参数可用于手写签名结果回调。

    /// <summary>    /// 保存为base64按钮文本/Save as Base64 button title    /// </summary>    [Parameter]    public string SaveBase64BtnTitle { get; set; } = "确定";    /// <summary>    /// 手写签名结果回调/SignaturePad result callback method    /// </summary>    [Parameter]    public EventCallback<string> OnResult { get; set; }

4.3 在 Blazor 调用组件页面中指定组件参数

仅获取手写签名结果回调

<SignaturePad OnResult="((e) =>  Result=e)" />@code{     public string? Result { get; set; }}

自定义按钮文本

<SignaturePad OnResult="((e) =>  Result=e)" SaveBase64BtnTitle="完成"/><SignaturePad OnResult="((e) =>  Result=e)" SaveBase64BtnTitle="OK" ClearBtnTitle="Clear"/><SignaturePad OnResult="((e) =>  Result=e)"               SignAboveLabel="Sign above"               UndoBtnTitle="Undo"               SaveBase64BtnTitle="OK"               ChangeColorBtnTitle="Change color"               ClearBtnTitle="Clear" />@code{     public string? Result { get; set; }}
Blazor组件自做四 : 使用JS隔离封装signature_pad签名组件

自定义按钮css

<SignaturePad OnResult="((e) =>  Result=e)" BtnCssClass="btn btn-outline-success"/>@code{     public string? Result { get; set; }}
Blazor组件自做四 : 使用JS隔离封装signature_pad签名组件

4.4 完整代码

razor代码

@implements IAsyncDisposable@namespace Blazor100.Components@inject IJSRuntime JS<div class="signature-pad-body">    <div @ref="SignaturepadElement" class="signature-pad">        <div class="signature-pad--body">            <canvas width="614" style="touch-action: none; user-select: none;" height="242"></canvas>        </div>        <div class="signature-pad--footer">            <div class="description">@SignAboveLabel</div>            <div class="signature-pad--actions">                <div>                    <button type="button" class="@BtnCssClass" data-action="clear">@ClearBtnTitle</button>                    @if (EnableChangeColorBtn)                    {                        <button type="button" class="@BtnCssClass" data-action="change-color">@ChangeColorBtnTitle</button>                    }                    <button type="button" class="@BtnCssClass" data-action="undo">@UndoBtnTitle</button>                </div>                <div>                    @if (EnableSaveBase64Btn)                    {                        <button type="button" class="@BtnCssClass" data-action="save-base64">@SaveBase64BtnTitle</button>                    }                    @if (EnableSavePNGBtn)                    {                        <button type="button" class="@BtnCssClass" data-action="save-png">@SavePNGBtnTitle</button>                    }                    @if (EnableSaveJPGBtn)                    {                        <button type="button" class="@BtnCssClass" data-action="save-jpg">@SaveJPGBtnTitle</button>                    }                    @if (EnableSaveSVGBtn)                    {                        <button type="button" class="@BtnCssClass" data-action="save-svg">@SaveSVGBtnTitle</button>                    }                </div>            </div>        </div>    </div></div>@code {    /// <summary>    /// 手写签名结果回调/SignaturePad result callback method    /// </summary>    [Parameter]    public EventCallback<string> OnResult { get; set; }    /// <summary>    /// 手写签名警告信息回调/SignaturePad alert callback method    /// </summary>    [Parameter]    public EventCallback<string> OnAlert { get; set; }    /// <summary>    /// 获得/设置 错误回调方法    /// </summary>    [Parameter]    public Func<string, Task>? OnError { get; set; }    /// <summary>    /// 在框内签名标签文本/Sign above label    /// </summary>    [Parameter]    public string SignAboveLabel { get; set; } = "在框内签名";    /// <summary>    /// 清除按钮文本/Clear button title    /// </summary>    [Parameter]    public string ClearBtnTitle { get; set; } = "清除";    /// <summary>    /// 请先签名提示文本/'Please provide a signature first' alert text    /// </summary>    [Parameter]    public string SignatureAlertText { get; set; } = "请先签名";    /// <summary>    /// 换颜色按钮文本/Change color button title    /// </summary>    [Parameter]    public string ChangeColorBtnTitle { get; set; } = "换颜色";    /// <summary>    /// 撤消按钮文本/Undo button title    /// </summary>    [Parameter]    public string UndoBtnTitle { get; set; } = "撤消";    /// <summary>    /// 保存为base64按钮文本/Save as Base64 button title    /// </summary>    [Parameter]    public string SaveBase64BtnTitle { get; set; } = "确定";    /// <summary>    /// 保存为PNG按钮文本/Save as PNG button title    /// </summary>    [Parameter]    public string SavePNGBtnTitle { get; set; } = "PNG";    /// <summary>    /// 保存为JPG按钮文本/Save as JPG button title    /// </summary>    [Parameter]    public string SaveJPGBtnTitle { get; set; } = "JPG";    /// <summary>    /// 保存为SVG按钮文本/Save as SVG button title    /// </summary>    [Parameter]    public string SaveSVGBtnTitle { get; set; } = "SVG";    /// <summary>    /// 启用换颜色按钮/Enable change color button    /// </summary>    [Parameter]    public bool EnableChangeColorBtn { get; set; } = true;    /// <summary>    /// 启用JS错误弹窗/Enable Alert from JS    /// </summary>    [Parameter]    public bool EnableAlertJS { get; set; } = true;    /// <summary>    /// 启用保存为base64按钮/Enable save as Base64 button    /// </summary>    [Parameter]    public bool EnableSaveBase64Btn { get; set; } = true;    /// <summary>    /// 启用保存为PNG按钮文本/Enable save as PNG button    /// </summary>    [Parameter]    public bool EnableSavePNGBtn { get; set; } = false;    /// <summary>    /// 启用保存为JPG按钮文本/Enable save as JPG button    /// </summary>    [Parameter]    public bool EnableSaveJPGBtn { get; set; } = false;    /// <summary>    /// 启用保存为SVG按钮文本/Enable save as SVG button    /// </summary>    [Parameter]    public bool EnableSaveSVGBtn { get; set; } = false;    /// <summary>    /// 按钮CSS式样/Button css style    /// </summary>    [Parameter]    public string BtnCssClass { get; set; } = "btn btn-light";    private IJSObjectReference? module;    /// <summary>    ///    /// </summary>    protected ElementReference SignaturepadElement { get; set; }    // To prevent making JavaScript interop calls during prerendering    protected override async Task OnAfterRenderAsync(bool firstRender)    {        if (!firstRender) return;        try        {            module = await JS.InvokeAsync<IJSObjectReference>("import", "./lib/signature_pad/app.js");            await module.InvokeVoidAsync("init", DotNetObjectReference.Create(this), SignaturepadElement, EnableAlertJS ? SignatureAlertText : null);        }        catch (Exception e)        {            if (OnError != null) await OnError.Invoke(e.Message);        }    }    [JSInvokable("signatureResult")]    public async Task SignatureResult(string val)    {        if (OnResult.HasDelegate) await OnResult.InvokeAsync(val);    }    [JSInvokable("signatureAlert")]    public async Task SignatureAlert()    {        if (OnResult.HasDelegate) await OnAlert.InvokeAsync(SignatureAlertText);    }    async ValueTask IAsyncDisposable.DisposeAsync()    {        if (module is not null)        {            //await module.InvokeVoidAsync("destroy",null);            await module.DisposeAsync();        }    }}

5. Pages文件添加SignaturePadPage.razor文件,用于演示组件调用.

SignaturePadPage.razor代码

@page "/signaturepad"<h3>SignaturePad 签名</h3><SignaturePad OnResult="((e) =>  Result=e)" /><p>签名Base64</p><textarea type="text" class="form-control" style="min-width: 100px;max-width: 80%;" rows="6"          @bind="Result"          placeholder="Base64" />@code{    /// <summary>    /// 签名Base64    /// </summary>    public string? Result { get; set; } }

6. _Imports.razor加入一行引用组件的命名空间.

@using Blazor100.Components

7. 首页引用组件演示页<SignaturePadPage />或者Shared/NavMenu.razor添加导航

        <div class="nav-item px-3">            <NavLink class="nav-link" href="signaturepad">                <span class="oi oi-plus" aria-hidden="true"></span> 手写签名2            </NavLink>        </div>

8. F5运行程序

9. Tips: 复杂签名会导致传输数据量大ssr会出现断流显示reload错误,启用以下配置解决这个问题.

        builder.Services.AddServerSideBlazor(a =>        {            //异步调用JavaScript函数的最大等待时间            a.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2);        }).AddHubOptions(o =>        {            //单个传入集线器消息的最大大小。默认 32 KB            o.MaximumReceiveMessageSize = null;            //可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。            o.StreamBufferCapacity = 20;        });

至此,使用JS隔离封装signature_pad签名组件大功告成! Happy coding!

Blazor组件自做系列

Blazor组件自做一 : 使用JS隔离封装viewerjs库

Blazor组件自做二 : 使用JS隔离制作手写签名组件

Blazor组件自做三 : 使用JS隔离封装ZXing扫码

Blazor组件自做四 : 使用JS隔离封装signature_pad签名组件

Blazor组件自做五: 使用JS隔离封装Google地图

Blazor组件自做六: 使用JS隔离封装Baidu地图

Blazor组件自做七: 使用JS隔离制作定位/持续定位组件

Blazor组件自做八: 使用JS隔离封装屏幕键盘kioskboard.js组件

项目源码Github|Gitee

  • © 2022 GitHub, Inc.
  • Terms
  • Privacy
  • Security
  • Status
  • Docs
  • Contact GitHub
  • Pricing
  • API
  • Training
  • Blog
  • About


6. _Imports.razor加入一行引用组件的命名空间.

@using Blazor100.Components

7. 首页引用组件演示页<SignaturePadPage />或者Shared/NavMenu.razor添加导航

        <div class="nav-item px-3">            <NavLink class="nav-link" href="signaturepad">                <span class="oi oi-plus" aria-hidden="true"></span> 手写签名2            </NavLink>        </div>

8. F5运行程序

9. Tips: 复杂签名会导致传输数据量大ssr会出现断流显示reload错误,启用以下配置解决这个问题.

        builder.Services.AddServerSideBlazor(a =>        {            //异步调用JavaScript函数的最大等待时间            a.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2);        }).AddHubOptions(o =>        {            //单个传入集线器消息的最大大小。默认 32 KB            o.MaximumReceiveMessageSize = null;            //可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。            o.StreamBufferCapacity = 20;        });

至此,使用JS隔离封装signature_pad签名组件大功告成! Happy coding!

Blazor组件自做系列

Blazor组件自做一 : 使用JS隔离封装viewerjs库

Blazor组件自做二 : 使用JS隔离制作手写签名组件

Blazor组件自做三 : 使用JS隔离封装ZXing扫码

Blazor组件自做四 : 使用JS隔离封装signature_pad签名组件

Blazor组件自做五:使用JS隔离封装Google地图

Blazor组件自做六:使用JS隔离封装Baidu地图

Blazor组件自做七:使用JS隔离制作定位/持续定位组件

Blazor组件自做八:使用JS隔离封装屏幕键盘kioskboard.js组件


项目源码Github|Gitee

https://github.com/densen2014/Blazor100

https://gitee.com/densen2014/Blazor100
Blazor100: Blazor入门100天

版权声明:内容来源于互联网和用户投稿 如有侵权请联系删除

本文地址:http://0561fc.cn/75218.html