1. 程式人生 > 其它 >Blazor元件自做二 : 使用JS隔離製作手寫簽名元件

Blazor元件自做二 : 使用JS隔離製作手寫簽名元件

Blazor元件自做二 : 使用JS隔離製作手寫簽名元件

Viewer.js庫是一個實用的js庫,用於圖片瀏覽,放大縮小翻轉幻燈片播放等實用操作

本文相關參考連結

  1. JavaScript 模組中的 JavaScript 隔離
  2. Viewer.js工程

Blazor JS 隔離優勢

匯入的 JS 不再汙染全域性名稱空間。
庫和元件的使用者不需要匯入相關的 JS。即不需要再在ssr的 Pages/_Host.cshtml 或 Pages/_Layout.cshtml ,wasm的 wwwroot/index.html 裡寫
第一遍載入靜態資產請求包含值為 no-cache 或 max-age(值為零 (0))的 標頭。真正頁面元件使用才載入真實大小檔案。

繼續Day2正文,以下基礎步驟再走一遍,Day3之後不再贅述.

1. 開啟VS2020, 新建工程面板, 專案模板搜尋 blazor , 選擇Blazor Server應用. (wasm也可以,但是不好除錯,先從簡單的SSR入手)
2. 工程名稱改為Blazor100,下一步,預設設定, 儲存.


3. 右鍵點選wwwroot資料夾,新增lib資料夾,新增handwritten子資料夾,裡面新增handwritten.js檔案, 新增handwritten.css檔案 . 最終版本參考如下


4. 編寫js檔案. 主要是使用Canvas畫線,附加功能可生成今日日期等等各位可以自行修改.
handwritten.js程式碼
export function init(wrapper, options) {
    console.log('start handwritten');



    /**
     * 格式化日期.
     */
    Date.prototype.format = function (fmt) {
        var o = {
            "M+": this.getMonth() + 1, //月份
            "d+": this.getDate(), //日
            "h+": this.getHours(), //小時
            "m+": this.getMinutes(), //分
            "s+": this.getSeconds(), //秒
            "q+": Math.floor((this.getMonth() + 3) / 3), //季度
            "S": this.getMilliseconds() //毫秒
        };
        if (/(y+)/.test(fmt)) {
            fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
        }
        for (var k in o) {
            if (new RegExp("(" + k + ")").test(fmt)) {
                fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ?
                    (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
            }
        }
        return fmt;
    }

    /**
     * 獲取URL引數
     */
    function getQueryString(name) {
        var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
        var r = window.location.search.substr(1).match(reg);
        if (r != null) return unescape(r[2]); return null;
    }
    /**
    * 是否數字
    */
    function isNumeric(n) {
        return !isNaN(parseFloat(n)) && isFinite(n);
    }

    function myRedirect(nextw) {
        event.returnValue = false;//加這句
        this.location.href = nextw;
    }
    //當頁面高度超過裝置可見高度時,阻止掉touchmove事件。
    document.body.addEventListener('touchmove', function (e) {
        e.preventDefault(); //阻止預設的處理方式(阻止下拉滑動的效果)
    }, { passive: false }); //passive 引數不能省略,用來相容ios和android 


    new lineCanvas({
        el: document.getElementById("canvas"), //繪製canvas的父級div
        clearEl: document.getElementById("clearCanvas"), //清除按鈕
        saveEl: document.getElementById("saveCanvas"), //儲存按鈕
        //      linewidth:1,//線條粗細,選填
        //      color:"black",//線條顏色,選填
        //      background:"#ffffff"//線條背景,選填
    });

    function lineCanvas(obj) {
        this.linewidth = 1;
        this.color = "#000000";
        this.background = "#ffffff";
        for (var i in obj) {
            this[i] = obj[i];
        };
        this.canvas = document.createElement("canvas");
        this.el.appendChild(this.canvas);
        this.cxt = this.canvas.getContext("2d");
        this.canvas.width = this.el.clientWidth;
        this.canvas.height = this.el.clientHeight;

        this.cxt.fillStyle = this.background;
        this.cxt.fillRect(0, 0, this.canvas.width, this.canvas.height);

        //this.cxt.fillStyle = "red";
        //this.cxt.font = "16px verdana";
        //this.cxt.textAlign = "left";

        ////fillText("要新增的文字",x0座標,y0座標)
        //var orderedtime = new Date().getTime();
        //orderedtime = (new Date(orderedtime)).format("yyyy-MM-dd  hh:mm");
        //this.cxt.fillText(orderedtime, 30, 30);

        this.cxt.fillStyle = this.background;
        this.cxt.strokeStyle = this.color;
        this.cxt.lineWidth = this.linewidth;
        this.cxt.lineCap = "round";
        //開始繪製
        this.canvas.addEventListener("touchstart", function (e) {
            this.cxt.beginPath();
            this.cxt.moveTo(e.changedTouches[0].pageX, e.changedTouches[0].pageY);
        }.bind(this), false);
        //繪製中
        this.canvas.addEventListener("touchmove", function (e) {
            this.cxt.lineTo(e.changedTouches[0].pageX, e.changedTouches[0].pageY);
            this.cxt.stroke();
        }.bind(this), false);
        //結束繪製
        this.canvas.addEventListener("touchend", function () {
            this.cxt.closePath();
        }.bind(this), false);
        //清除畫布
        this.clearEl.addEventListener("click", function () {
            this.cxt.clearRect(0, 0, this.canvas.width, this.canvas.height);
        }.bind(this), false);
        //儲存圖片,直接轉base64
        this.saveEl.addEventListener("click", function () {
            var imgBase64 = this.canvas.toDataURL();
            console.log(imgBase64);
            return wrapper.invokeMethodAsync("invokeFromJS", imgBase64);
        }.bind(this), false);
        //新增日期時間
        function adddatetime() {
            this.cxt.fillStyle = "red";
            this.cxt.font = "12px '微軟雅黑'";
            this.cxt.textAlign = "left";
            //fillText("要新增的文字",x0座標,y0座標)
            var orderedtime = new Date().getTime();
            orderedtime = (new Date(orderedtime)).format("yyyy-MM-dd  hh:mm");
            this.cxt.strokeText(orderedtime, 50, 100);
        }
    };


}

export function destroy(options) { 
}
5. 編寫 handwritten.css 檔案. (細心的朋友應該發現,跟Day1不一樣,這個css放在lib裡面,留著大家思考 :-> )
handwritten.css程式碼
#canvas {
    width: 99%;
    /*max-width: 375px;*/
    height: 300px;
    position: relative;
    overflow: hidden;
    overflow: -Scroll;
}

    #canvas canvas {
        display: block;
    }

#clearCanvas0 {
    width: calc(50% - 5px);
    height: 40px;
    line-height: 40px;
    text-align: center;
    position: absolute;
    top: 300px;
    left: 5px;
    border: 1px solid #DEDEDE;
    z-index: 1;
}

#saveCanvas0 {
    width: calc(50% - 5px);
    height: 40px;
    line-height: 40px;
    text-align: center;
    position: absolute;
    top: 300px;
    right: 5px;
    border: 1px solid #DEDEDE;
    z-index: 1;
}
6. 點開或者新建Components資料夾 , 新建Handwritten.razor元件,簽名會直接轉化為Base64編碼的string,同學們自己儲存到資料庫或者當作變數傳遞就行

元件的名稱空間統一使用Blazor100.Components,在razor檔案和razor.cs都使用統一名稱空間,這樣不會受到資料夾巢狀各種影響.

Handwritten.razor程式碼
@implements IAsyncDisposable
@namespace Blazor100.Components
@inject IJSRuntime JS

<link href="lib/handwritten/handwritten.css" rel="stylesheet" />

<div class="modal alert-popup" tabindex="-1" style="display:block" role="dialog">
    <div class="modal-dialog-w100">
        <div class="modal-content">
            <!-- Edit form for the current item -->
            <div id="canvas" style="height: 300px;">
            </div>
            <div>
                <button class="btn btn-secondary p-2 m-1 w-25" id="clearCanvas">清除</button>
                <button class="btn btn-primary p-2 m-1 w-25" id="saveCanvas">儲存</button>
            </div>
        </div>
    </div>
</div>
@Result


@code {
    /// <summary>
    /// Handwritten 手寫簽名
    /// </summary>
    [Parameter]
    public EventCallback<string> HandwrittenBase64 { get; set; }

    /// <summary>
    /// 關閉掃碼框回撥方法
    /// </summary>
    [Parameter]
    public EventCallback Close { get; set; }


    /// <summary>
    /// 簽名結果,簽名會直接轉化為Base64編碼的string,儲存到資料庫或者當作變數傳遞都可以
    /// </summary>
    [Parameter]
    public string? Result { get; set; }

    private IJSObjectReference? module;

    // To prevent making JavaScript interop calls during prerendering
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (!firstRender) return;
        module = await JS.InvokeAsync<IJSObjectReference>("import", "./lib/handwritten/handwritten.js");
        await module.InvokeVoidAsync("init", DotNetObjectReference.Create(this), null);
    }

    [JSInvokable("invokeFromJS")]
    public async Task ChangeValue(string val)
    {
        Result = val;
        StateHasChanged();
        await HandwrittenBase64.InvokeAsync(val);
        //return Task.CompletedTask;
    }


    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            //await module.InvokeVoidAsync("destroy",null);
            await module.DisposeAsync();
        }
    }
}

7. Pages檔案新增HandwrittenPage.razor檔案,用於演示元件呼叫.
HandwrittenPage.razor程式碼
@page "/handwritten"

<h3>Handwritten 手寫簽名</h3>
<h6>注意:只支援移動裝置簽名,桌面版瀏覽器測試請開啟F12模擬為移動裝置.</h6>

<button class="btn btn-primary"
        type="button"
        @onclick="(() => ShowHandwritten = !ShowHandwritten)">
    [簽名]
</button>
<textarea type="text" class="form-control" style="min-width: 100px;" rows="10"
          @bind="DrawBase64"
          placeholder="Base64" />
@if (ShowHandwritten)
{

    <Handwritten HandwrittenBase64="(e => { DrawBase64=e; ShowHandwritten = !ShowHandwritten; })"
                 Close="(()=>ShowHandwritten=!ShowHandwritten)" />

}

@code{

    /// <summary>
    /// 顯示簽名介面
    /// </summary>
    bool ShowHandwritten { get; set; } = false;

    /// <summary>
    /// 簽名Base64
    /// </summary>
    public string? DrawBase64 { get; set; }


}

8. _Imports.razor加入一行引用元件的名稱空間,已經有這行就不需要再重複寫了.
@using Blazor100.Components
9. 首頁引用元件演示頁 <HandwrittenPage />
10. F5執行程式,將會自動開啟瀏覽器除錯

簽名結果會直接轉化為Base64編碼的string,儲存到資料庫或者當作變數傳遞都可以

至此,使用JS隔離製作手寫簽名元件大功告成! Happy coding!

專案原始碼 Github | Gitee