Blazor元件自做二 : 使用JS隔離製作手寫簽名元件
阿新 • • 發佈:2022-03-21
Blazor元件自做二 : 使用JS隔離製作手寫簽名元件
Viewer.js庫是一個實用的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,儲存到資料庫或者當作變數傳遞都可以