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

本文相关参考链接

  1. JavaScript 模块中的 JavaScript 隔离
  2. Viewer.js工程
  3. Blazor组件自做一 : 使用JS隔离封装viewerjs库

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代码
  1. export function init(wrapper, options) {
  2. console.log('start handwritten');
  3. /**
  4. * 格式化日期.
  5. */
  6. Date.prototype.format = function (fmt) {
  7. var o = {
  8. "M+": this.getMonth() + 1, //月份
  9. "d+": this.getDate(), //日
  10. "h+": this.getHours(), //小时
  11. "m+": this.getMinutes(), //分
  12. "s+": this.getSeconds(), //秒
  13. "q+": Math.floor((this.getMonth() + 3) / 3), //季度
  14. "S": this.getMilliseconds() //毫秒
  15. };
  16. if (/(y+)/.test(fmt)) {
  17. fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
  18. }
  19. for (var k in o) {
  20. if (new RegExp("(" + k + ")").test(fmt)) {
  21. fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ?
  22. (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
  23. }
  24. }
  25. return fmt;
  26. }
  27. /**
  28. * 获取URL参数
  29. */
  30. function getQueryString(name) {
  31. var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
  32. var r = window.location.search.substr(1).match(reg);
  33. if (r != null) return unescape(r[2]); return null;
  34. }
  35. /**
  36. * 是否数字
  37. */
  38. function isNumeric(n) {
  39. return !isNaN(parseFloat(n)) && isFinite(n);
  40. }
  41. function myRedirect(nextw) {
  42. event.returnValue = false;//加这句
  43. this.location.href = nextw;
  44. }
  45. //当页面高度超过设备可见高度时,阻止掉touchmove事件。
  46. document.body.addEventListener('touchmove', function (e) {
  47. e.preventDefault(); //阻止默认的处理方式(阻止下拉滑动的效果)
  48. }, { passive: false }); //passive 参数不能省略,用来兼容ios和android
  49. new lineCanvas({
  50. el: document.getElementById("canvas"), //绘制canvas的父级div
  51. clearEl: document.getElementById("clearCanvas"), //清除按钮
  52. saveEl: document.getElementById("saveCanvas"), //保存按钮
  53. // linewidth:1,//线条粗细,选填
  54. // color:"black",//线条颜色,选填
  55. // background:"#ffffff"//线条背景,选填
  56. });
  57. function lineCanvas(obj) {
  58. this.linewidth = 1;
  59. this.color = "#000000";
  60. this.background = "#ffffff";
  61. for (var i in obj) {
  62. this[i] = obj[i];
  63. };
  64. this.canvas = document.createElement("canvas");
  65. this.el.appendChild(this.canvas);
  66. this.cxt = this.canvas.getContext("2d");
  67. this.canvas.width = this.el.clientWidth;
  68. this.canvas.height = this.el.clientHeight;
  69. this.cxt.fillStyle = this.background;
  70. this.cxt.fillRect(0, 0, this.canvas.width, this.canvas.height);
  71. //this.cxt.fillStyle = "red";
  72. //this.cxt.font = "16px verdana";
  73. //this.cxt.textAlign = "left";
  74. ////fillText("要添加的文字",x0坐标,y0坐标)
  75. //var orderedtime = new Date().getTime();
  76. //orderedtime = (new Date(orderedtime)).format("yyyy-MM-dd hh:mm");
  77. //this.cxt.fillText(orderedtime, 30, 30);
  78. this.cxt.fillStyle = this.background;
  79. this.cxt.strokeStyle = this.color;
  80. this.cxt.lineWidth = this.linewidth;
  81. this.cxt.lineCap = "round";
  82. //开始绘制
  83. this.canvas.addEventListener("touchstart", function (e) {
  84. this.cxt.beginPath();
  85. this.cxt.moveTo(e.changedTouches[0].pageX, e.changedTouches[0].pageY);
  86. }.bind(this), false);
  87. //绘制中
  88. this.canvas.addEventListener("touchmove", function (e) {
  89. this.cxt.lineTo(e.changedTouches[0].pageX, e.changedTouches[0].pageY);
  90. this.cxt.stroke();
  91. }.bind(this), false);
  92. //结束绘制
  93. this.canvas.addEventListener("touchend", function () {
  94. this.cxt.closePath();
  95. }.bind(this), false);
  96. //清除画布
  97. this.clearEl.addEventListener("click", function () {
  98. this.cxt.clearRect(0, 0, this.canvas.width, this.canvas.height);
  99. }.bind(this), false);
  100. //保存图片,直接转base64
  101. this.saveEl.addEventListener("click", function () {
  102. var imgBase64 = this.canvas.toDataURL();
  103. console.log(imgBase64);
  104. return wrapper.invokeMethodAsync("invokeFromJS", imgBase64);
  105. }.bind(this), false);
  106. //添加日期时间
  107. function adddatetime() {
  108. this.cxt.fillStyle = "red";
  109. this.cxt.font = "12px '微软雅黑'";
  110. this.cxt.textAlign = "left";
  111. //fillText("要添加的文字",x0坐标,y0坐标)
  112. var orderedtime = new Date().getTime();
  113. orderedtime = (new Date(orderedtime)).format("yyyy-MM-dd hh:mm");
  114. this.cxt.strokeText(orderedtime, 50, 100);
  115. }
  116. };
  117. }
  118. export function destroy(options) {
  119. }
5. 编写 handwritten.css 文件. (细心的朋友应该发现,跟Day1不一样,这个css放在lib里面,留着大家思考 :-> )
handwritten.css代码
  1. #canvas {
  2. width: 99%;
  3. /*max-width: 375px;*/
  4. height: 300px;
  5. position: relative;
  6. overflow: hidden;
  7. overflow: -Scroll;
  8. }
  9. #canvas canvas {
  10. display: block;
  11. }
  12. #clearCanvas0 {
  13. width: calc(50% - 5px);
  14. height: 40px;
  15. line-height: 40px;
  16. text-align: center;
  17. position: absolute;
  18. top: 300px;
  19. left: 5px;
  20. border: 1px solid #DEDEDE;
  21. z-index: 1;
  22. }
  23. #saveCanvas0 {
  24. width: calc(50% - 5px);
  25. height: 40px;
  26. line-height: 40px;
  27. text-align: center;
  28. position: absolute;
  29. top: 300px;
  30. right: 5px;
  31. border: 1px solid #DEDEDE;
  32. z-index: 1;
  33. }
6. 点开或者新建Components文件夹 , 新建Handwritten.razor组件,签名会直接转化为Base64编码的string,同学们自己保存到数据库或者当作变量传递就行

组件的命名空间统一使用Blazor100.Components,在razor文件和razor.cs都使用统一命名空间,这样不会受到文件夹嵌套各种影响.

Handwritten.razor代码
  1. @implements IAsyncDisposable
  2. @namespace Blazor100.Components
  3. @inject IJSRuntime JS
  4. <link href="lib/handwritten/handwritten.css" rel="stylesheet" />
  5. <div class="modal alert-popup" tabindex="-1" style="display:block" role="dialog">
  6. <div class="modal-dialog-w100">
  7. <div class="modal-content">
  8. <!-- Edit form for the current item -->
  9. <div id="canvas" style="height: 300px;">
  10. </div>
  11. <div>
  12. <button class="btn btn-secondary p-2 m-1 w-25" id="clearCanvas">清除</button>
  13. <button class="btn btn-primary p-2 m-1 w-25" id="saveCanvas">保存</button>
  14. </div>
  15. </div>
  16. </div>
  17. </div>
  18. @Result
  19. @code {
  20. /// <summary>
  21. /// Handwritten 手写签名
  22. /// </summary>
  23. [Parameter]
  24. public EventCallback<string> HandwrittenBase64 { get; set; }
  25. /// <summary>
  26. /// 关闭扫码框回调方法
  27. /// </summary>
  28. [Parameter]
  29. public EventCallback Close { get; set; }
  30. /// <summary>
  31. /// 签名结果,签名会直接转化为Base64编码的string,保存到数据库或者当作变量传递都可以
  32. /// </summary>
  33. [Parameter]
  34. public string? Result { get; set; }
  35. private IJSObjectReference? module;
  36. // To prevent making JavaScript interop calls during prerendering
  37. protected override async Task OnAfterRenderAsync(bool firstRender)
  38. {
  39. if (!firstRender) return;
  40. module = await JS.InvokeAsync<IJSObjectReference>("import", "./lib/handwritten/handwritten.js");
  41. await module.InvokeVoidAsync("init", DotNetObjectReference.Create(this), null);
  42. }
  43. [JSInvokable("invokeFromJS")]
  44. public async Task ChangeValue(string val)
  45. {
  46. Result = val;
  47. StateHasChanged();
  48. await HandwrittenBase64.InvokeAsync(val);
  49. //return Task.CompletedTask;
  50. }
  51. async ValueTask IAsyncDisposable.DisposeAsync()
  52. {
  53. if (module is not null)
  54. {
  55. //await module.InvokeVoidAsync("destroy",null);
  56. await module.DisposeAsync();
  57. }
  58. }
  59. }
7. Pages文件添加HandwrittenPage.razor文件,用于演示组件调用.
HandwrittenPage.razor代码
  1. @page "/handwritten"
  2. <h3>Handwritten 手写签名</h3>
  3. <h6>注意:只支持移动设备签名,桌面版浏览器测试请打开F12模拟为移动设备.</h6>
  4. <button class="btn btn-primary"
  5. type="button"
  6. @onclick="(() => ShowHandwritten = !ShowHandwritten)">
  7. [签名]
  8. </button>
  9. <textarea type="text" class="form-control" style="min-width: 100px;" rows="10"
  10. @bind="DrawBase64"
  11. placeholder="Base64" />
  12. @if (ShowHandwritten)
  13. {
  14. <Handwritten HandwrittenBase64="(e => { DrawBase64=e; ShowHandwritten = !ShowHandwritten; })"
  15. Close="(()=>ShowHandwritten=!ShowHandwritten)" />
  16. }
  17. @code{
  18. /// <summary>
  19. /// 显示签名界面
  20. /// </summary>
  21. bool ShowHandwritten { get; set; } = false;
  22. /// <summary>
  23. /// 签名Base64
  24. /// </summary>
  25. public string? DrawBase64 { get; set; }
  26. }
8. _Imports.razor加入一行引用组件的命名空间,已经有这行就不需要再重复写了.
  1. @using Blazor100.Components

9. 首页引用组件演示页 <HandwrittenPage />

10. F5运行程序,将会自动打开浏览器调试

签名结果会直接转化为Base64编码的string,保存到数据库或者当作变量传递都可以

至此,使用JS隔离制作手写签名组件大功告成! Happy coding!

Blazor组件自做系列

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

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

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

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

Blazor组件自做五: 使用JS隔离封装Google地图<03-24>

Blazor组件自做六: 使用JS隔离封装Baidu地图<03-25>

Blazor组件自做七: 使用JS隔离制作定位/持续定位组件<03-26>

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

项目源码 Github | Gitee

Blazor组件自做二 : 使用JS隔离制作手写签名组件的更多相关文章

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

    1. 运行截图 演示地址 2. 在文件夹wwwroot/lib,添加kioskboard子文件夹,添加kioskboards.js文件 2.1 常规操作,懒加载js库, export function ...

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

    Viewer.js库是一个实用的js库,用于图片浏览,放大缩小翻转幻灯片播放等实用操作 本文相关参考链接 JavaScript 模块中的 JavaScript 隔离 Viewer.js工程 Blazo ...

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

    Blazor组件自做三 : 使用JS隔离封装ZXing扫码 本文基础步骤参考前两篇文章 Blazor组件自做一 : 使用JS隔离封装viewerjs库 Blazor组件自做二 : 使用JS隔离制作手写 ...

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

    运行截图 演示地址 响应式演示 感谢szimek写的棒棒的signature_pad.js项目, 来源: https://github.com/szimek/signature_pad 正式开始 1. ...

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

    Blazor组件自做五: 使用JS隔离封装Google地图 运行截图 演示地址 正式开始 1. 谷歌地图API 谷歌开发文档 开始学习 Maps JavaScript API 的最简单方法是查看一个简 ...

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

    1. 运行截图 演示地址 2. 在文件夹wwwroot/lib,添加baidu子文件夹,添加baidumap.js文件 2.1 跟上一篇类似,用代码方式异步加载API,脚本生成新的 body > ...

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

    1. 运行截图 演示地址 2. 在文件夹wwwroot/lib,添加geolocation子文件夹,添加geolocation.js文件 本组件主要是调用浏览器两个API实现基于浏览器的定位功能,现代 ...

  8. 微信小程序:手写日历组件

    一.前言 最近公司要做一个酒店入住的小程序,不可避免的一定会使用到日历,而小程序没有内置的日历组件.在网上看了一下也没有非常适合需求的日历,于是自己写了一个. 二.代码 1. 原理分析 写一个日历只需 ...

  9. 利用html 5 websocket做个山寨版web聊天室(手写C#服务器)

    在之前的博客中提到过看到html5 的websocket后很感兴趣,终于可以摆脱长轮询(websocket之前的实现方式可以看看Developer Works上的一篇文章,有简单提到,同时也说了web ...

随机推荐

  1. PHP动态修改配置文件

    文件结构: index.php 主页 config 配置文件 doUpdate.php 修改功能页 index.php <html> <head> <title>修 ...

  2. vue 控制台错误

    控制台显示报错: Uncaught TypeError: WEBPACK_IMPORTED_MODULE_1_vuex.a.store is not a constructor 解决办法: 将new ...

  3. Nginx解决跨域问题No 'Access-Control-Allow-Origin'

    使用nginx在server块下的location块下为请求添加请求头来解决跨域 add_header 'Access-Control-Allow-Origin' '*'; add_header 'A ...

  4. 10、mysql的调优

    mysql的调优 调优的最终目的:节省系统资源.提高响应速度下面从6个维度对mysql进行优化. 第一步:对服务器的参数进行调优 通过show profile命令分析,如果sql语句在执行过程中等待时 ...

  5. Spring——自动装配的三种实现方式

    依赖注入的本质是装配,装配是依赖注入的具体行为 spring会在上下文中自动寻找,并自动给bean装配属性 自动装配的三种方式 (1).在xml中显式的装配 (2).在java中显式的装配 (3).隐 ...

  6. 在idea中新建完springboot项目的时候遇到问题(右键没有class选择;控制台报错:Could not transfer artifact org.apache.tomcat.embed:tomcat-embed-core:jar:9.0.60 from/to central ....)

    一.在idea中新建完springboot项目的时候遇到问题 问题1:右键没有class选择 解决:之所以会如此,是因为项目还没完成创建完成,解决:只需等等即可,等到完全创建完成即可 问题2(报tom ...

  7. CentOS7安装redis5

    1.下载/解压redisredis手册地址:http://redisdoc.com/下载路径:https://redis.io/downloadtar zxvf redis包名 2.编译&安装 ...

  8. synchronized底层实现原理及锁优化

    一.概述 1.synchronized作用 原子性:synchronized保证语句块内操作是原子的 可见性:synchronized保证可见性(通过"在执行unlock之前,必须先把此变量 ...

  9. elasticsearch 索引数据多了怎么办,如何调优,部署 ?

    面试官:想了解大数据量的运维能力. 解答:索引数据的规划,应在前期做好规划,正所谓"设计先行,编码在后", 这样才能有效的避免突如其来的数据激增导致集群处理能力不足引发的线上客户 ...

  10. 模糊查询like语句该怎么写?

    第1种:在Java代码中添加sql通配符. string wildcardname = "%smi%"; list<name> names = mapper.selec ...