C# 结合JavaScript实现手写板签名并上传到服务器

应用场景

我们最近开发了一款笔迹测试功能的程序(测试版),用户在手写板上手写签名,提交后即可测试出被测试者的心理素质评价分析。类似功能的场景还比如,在银行柜台办理业务,期间可能需要您使用手写设备进行签名并确认;保险续期小程序,到期后需要你在确认续期条款后,在手机上提供的签名区域进行签名并提交确认。

实现效果

笔迹测试显示界面如下:

可选择画笔颜色(默认为黑色笔) ,在虚线框内可随便写一段文字,点击提交即可。当然程序还提供拍照上传功能,这里不再详述。下面我们开始介绍,C#如何结合JavaScript实现手写板写字并上传到服务器进行处理。

开发运行环境

操作系统: Windows Server 2019 DataCenter

手写触屏设备:Microsoft Surface Pro 9

.net版本: .netFramework4.0 或以上

开发工具:VS2019  C#

设计实现

手写功能

设计采用了 iframe 嵌入式的方式实现 JavaScript 前端,假设页面为 hw.aspx ,该页面实现了手写功能、重写功能、画笔选择功能和提交功能,其完整示例代码如下:

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=yes"/><title>手写板</title><style type="text/css">html,body{margin: 0;padding: 0;}.saveimg{text-align: center;}.saveimgs span{display: inline-block;margin-top:5px;}</style>
</head>
<body><script src="jquery-3.3.1.min.js"></script><div align="center"><canvas id="myCanvas" width="500" height="300" style="border:1px dotted #6699cc"></canvas><div class="control-ops control"><button type="button" class="btn btn-primary" onclick="javascript:clearArea();return false;">重写</button><select style="display:none" id="selWidth" onchange="aaa()"><option value="1">1</option><option value="3" selected="selected">3</option><option value="5">5</option><option value="7">7</option><option value="9">9</option><option value="11">11</option></select><select id="selColor" onchange="aaa2()"><option value="black" selected="selected">黑色笔</option><option value="blue">蓝色笔</option><option value="red">红色笔</option><option value="green">绿色笔</option><option value="yellow">黄色笔</option><option value="gray">深灰笔</option></select><button type="button" class="saveimg" onclick="javascript:saveImageInfo();return false;">提交</button></div><div class="saveimgs"></div>
</div></body><script type="text/javascript">var mousePressed = false;var lastX, lastY;var ctx = document.getElementById('myCanvas').getContext("2d");var c = document.getElementById("myCanvas");var control = document.getElementsByClassName("control")[0];var saveimgs = document.getElementsByClassName("saveimgs")[0];window.onload = function () {document.getElementById('myCanvas').setAttribute("width", $(window).width()-5);InitThis();}function saveImageInfo(){var image = c.toDataURL("image/png");window.parent.document.getElementById('pbase64').value = image;window.parent.document.getElementById('phw').click();return;var ctximg = document.createElement("span");ctximg.innerHTML = "<img src='"+image+"' alt='from canvas'/>";if(saveimgs.getElementsByTagName('span').length >= 1){var span_old = saveimgs.getElementsByTagName("span")[0];saveimgs.replaceChild(ctximg,span_old)}else{saveimgs.appendChild(ctximg);}}var selected1,selected2;function aaa(){var sel = document.getElementById('selWidth');var value = sel.selectedIndex;return selected1 = sel[value].value;}function aaa2(){var sel2 = document.getElementById('selColor');var value = sel2.selectedIndex;return selected2 = sel2[value].value;}function InitThis() {
//          触摸屏c.addEventListener('touchstart', function (event) {console.log(1)if (event.targetTouches.length == 1) {event.preventDefault();// 阻止浏览器默认事件,重要var touch = event.targetTouches[0];mousePressed = true;Draw(touch.pageX - this.offsetLeft, touch.pageY - this.offsetTop, false);}},false);c.addEventListener('touchmove', function (event) {console.log(2)if (event.targetTouches.length == 1) {event.preventDefault();// 阻止浏览器默认事件,重要var touch = event.targetTouches[0];if (mousePressed) {Draw(touch.pageX - this.offsetLeft, touch.pageY - this.offsetTop, true);}}},false);c.addEventListener('touchend', function (event) {console.log(3)if (event.targetTouches.length == 1) {event.preventDefault();// 阻止浏览器默认事件,防止手写的时候拖动屏幕,重要mousePressed = false;}},false);/*c.addEventListener('touchcancel', function (event) {console.log(4)mousePressed = false;},false);*///         鼠标c.onmousedown = function (event) {mousePressed = true;Draw(event.pageX - this.offsetLeft, event.pageY - this.offsetTop, false);};c.onmousemove = function (event) {if (mousePressed) {Draw(event.pageX - this.offsetLeft, event.pageY - this.offsetTop, true);}};c.onmouseup = function (event) {mousePressed = false;};}function Draw(x, y, isDown) {if (isDown) {ctx.beginPath();ctx.strokeStyle = selected2;ctx.lineWidth = selected1;ctx.lineJoin = "round";ctx.moveTo(lastX, lastY);ctx.lineTo(x, y);ctx.closePath();ctx.stroke();}lastX = x; lastY = y;}function clearArea() {ctx.setTransform(1, 0, 0, 1, 0, 0);ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);// 清除签名图片if(saveimgs.getElementsByTagName('span').length >= 1){var clearImg = saveimgs.getElementsByTagName('span')[0];saveimgs.removeChild(clearImg);}}
</script></html>

该页面需要引用 jquery-3.3.1.min.js ,可下载我的链接资源:

https://download.csdn.net/download/michaelline/89226046

前端引用

前端页面除嵌入手写功能页面外,iframe的父窗口需要放置两个元素,一个用于存储手写提交后的Base64数据的 Asp.net 服务器按钮文本框元素,另一个是用于模拟调用服务器事件的 Asp.net 服务器按钮元素。

引用代码如下:

<div  style=" text-align:center"><iframe width="520" height="350" id="hw" runat="server" scrolling="no" frameborder="0" src="/cc/module/hw/hw.aspx" ></iframe><asp:TextBox ID="pbase64" TextMode="MultiLine" style="display:none" runat="server" ></asp:TextBox><asp:button ID="phw" OnClientClick="waittip()" text="后台处理" runat="server" style="display:none" onclick="phw_Click" />
</div>

后端处理

手写功能中的提交执行代码将调用如下:

window.parent.document.getElementById('pbase64').value = image;
window.parent.document.getElementById('phw').click();

其中 pbase64 和 phw 控件为服务器控件,可直接模拟调用 phw 按钮的服务器 click,在这之前其还可以自动处理 OnClientClick事件以显示等待界面。请注意 waittip() 执行了一段 javascript 脚本,如下:

function waittip() {layer.open({ type: 2, shadeClose: false, content: '正在分析,请稍候...' });
}

 这其中引入了 Layer 弹出层技术,关于 Layer 弹层组件请参照我的文章《改造 layer 弹层移动版组件》,下载JS请访问如下链接:

https://download.csdn.net/download/michaelline/88406984

这是调用服务器Click的事件处理代码,将上传的Base64图片转为两种格式的图片文件(PNG和JPEG)。代码如下:

    protected void phw_Click(object sender, EventArgs e){string mtfilename = "d:\\hw_" + System.Guid.NewGuid().ToString() + ".png";string mtfilename2 = "d:\\hw_" + System.Guid.NewGuid().ToString() + ".jpg";string dummyData = pbase64.Text.Trim().Replace("data:image/png;base64,", "");Base64StringToImage(dummyData, mtfilename);if (File.Exists(mtfilename)==false){base64.ImageUrl = dummyData;layer.open("保存手写图片失败,请重试并提交。" , "'确定'", "error");return;}System.Drawing.Image img =  System.Drawing.Image.FromFile(mtfilename);using (var b = new System.Drawing.Bitmap(img.Width, img.Height)){b.SetResolution(img.HorizontalResolution, img.VerticalResolution);using (var g = System.Drawing.Graphics.FromImage(b)){g.Clear(System.Drawing.Color.White);g.DrawImageUnscaled(img, 0, 0);}b.Save(mtfilename2, System.Drawing.Imaging.ImageFormat.Jpeg);}}

小结

本示例中的前后端代码仅为展示参考,手写功能在支持触屏的设备可以支持手写,也可以用鼠标进行模拟。

服务器调用示例中需要使用 Base64StringToImage(dummyData, mtfilename); 方法由Base64数据转化为图片文件,代码如下:

public bool Base64StringToImage(string strbase64, string outputFilename)
{byte[] arr = Convert.FromBase64String(strbase64);MemoryStream ms = new MemoryStream(arr);System.Drawing.Image img = System.Drawing.Image.FromStream(ms);img.Save(outputFilename);img.Dispose();if (File.Exists(outputFilename)){return true;}return false;
}

感谢您的阅读,希望本文能够对您有所帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/3676.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023最新!nginx安装配置保姆级教程

2023最新!nginx安装配置保姆级教程 这篇文章了参考了这位的教程:https://blog.csdn.net/qq_36838700/article/details/129971765 导航 文章目录 2023最新!nginx安装配置保姆级教程一、nginx下载二、编译安装nginx安装pcre安装openssl、zlib、gcc依赖安装nginx 二、拓展 一、n…

低空经济+飞行汽车:载人无人机技术详解

低空经济与飞行汽车是近年来备受关注的话题。随着科技的不断进步&#xff0c;尤其是无人机技术的快速发展&#xff0c;飞行汽车已经从科幻概念逐渐变为现实。以下是对低空经济与飞行汽车&#xff0c;特别是载人无人机技术的详解&#xff1a; 1. 低空经济&#xff1a; 定义&…

javaEE--多线程学习-进程调度

进程调度不明白&#xff1f;看这一篇文章就够了&#xff0c;逻辑衔接严密&#xff0c;文末附有关键面试题&#xff0c;一个海后的小故事让你瞬间明白这里面的弯弯绕绕&#xff01; 目录 1.什么是进程&#xff1f; 2.进程控制块&#xff08;PCB&#xff09; 2.1 一个PCB就是一…

已解决java.lang.IllegalThreadStateException: 非法线程状态异常的正确解决方法,亲测有效!!!

已解决java.lang.IllegalThreadStateException: 非法线程状态异常的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 场景描述 报错原因 解决思路 解决方法 检查线程状态 正确管理线程生命周期 异常处理 总结 博主v&#xff1a…

JavaEE >> Spring Boot(1)

Spring Boot 前面已经介绍了 Spring &#xff0c;是为了简化 Java 程序开发的&#xff0c;而在前面创建的过程中就会发现其实 Spring 还是有点复杂&#xff0c;此时 Spring Boot 就诞生了&#xff0c; Spring Boot 是为了简化 Spring 程序开发的。 Spring Boot 即 Spring 脚手…

history日志发送到远程日志服务器

主要目标是设置history信息包含谁、源IP、在哪个目录下、做了什么工作&#xff0c;并实时将日志发送到日志审计服务。 &#xff08;一&#xff09;基础知识 1.logger 是一个shell接口&#xff0c;可以通过该接口使用rsyslog的日志模块 2./etc/profile.d/history.sh比配置/etc…

Android某钉数据库的解密分析

声明 1 本文章中所有内容仅供学习交流&#xff0c;抓包内容、敏感网址、数据接口均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 目的 1 解密app数据库&#xff0c;用数据库软件打开查看信息内容 入手…

【后端】python与django的开发环境搭建指南

安装Git 双击Git 客户端安装文件&#xff0c;在安装页面&#xff0c;单击“Next” 在安装路径选择页面&#xff0c;保持默认&#xff0c;单击“Next” 在功能组件选择页面&#xff0c;保持默认&#xff0c;单击“Next” 在开始菜单文件夹设置页面&#xff0c;保持默认&am…

浅谈rDNS在IP情报建设中的应用

在当今数字化世界中&#xff0c;互联网已经成为人们日常生活和商业活动中不可或缺的一部分。在这个庞大而复杂的网络生态系统中&#xff0c;IP地址是连接和识别各种网络设备和服务的基础。然而&#xff0c;仅仅知道一个设备的IP地址并不足以充分理解其在网络中的角色和行为。为…

win11 桌面图标突然多 绿色小对勾,如何去除掉

突然间桌面图标每个上面都有一个绿色小狗狗&#xff0c;如下图所示&#xff0c;以为中病毒了&#xff0c;后来一查不是。 去除方法 1、鼠标在桌面空白处单击&#xff0c;选择“个性化”&#xff08;或直接按“windows键I键”&#xff09;调出设置菜单。 2、在左侧选择“主题”…

C++:const成员和取地址操作符

目录 一、const成员 二、取地址及const取地址操作符重载 一、const成员 将const修饰的“成员函数”称之为const成员函数&#xff0c;const修饰类成员函数&#xff0c;实际修饰该成员函数 隐含的this指针&#xff0c;表明在该成员函数中不能对类的任何成员进行修改。 注&…

21.基础乐理-等音调扩展篇、为何一共十五个大调

首先 等音调 的概念是基于 等音 的概念&#xff0c;比如下图中的音名&#xff1a;因为用的按键相同&#xff0c;音名不同&#xff0c;所以被称为等音调 然后音名一共有35个&#xff0c;如下图&#xff1a;所以在理论上它会有35个大调&#xff0c;但是人总是倾向于选择简单、简洁…

MAC如何重装系统(怒冲30大洋,才拿到的教程~,收藏点赞兄弟们)

背景 应该是之前装了一些远程的软件&#xff0c;卸载一直不干净&#xff0c;导致电脑很卡&#xff0c;而且网络貌似出现了问题&#xff0c;钉钉直接登陆不上了。其余软件网络倒是还好。所以就去PDD&#xff0c;买了个教程&#xff0c;重装了一下。才发现是mac自带&#xff0c;…

云赛道---AI开发框架

MindSpore 旨在提供端边云全场景的 AI 框架。 MindSpore 可部署于端、边、云不同的 硬件环境&#xff0c;满足不同环境的差异化需求&#xff0c;如支持端侧的轻量化部署&#xff0c;支持云侧丰富的 训练功能如自动微分、混合精度、模型易用编程等。 MindSpore 全场景的几个重…

交互式探索微生物群落与生态功能的关系

微生物群落在生态系统中发挥则重要功能&#xff0c;我们在对微生物群落进行分析时&#xff0c;会将不同分类水平&#xff08;从门到属&#xff09;的微生物类群的相对丰度与测定的某一生态功能进行相关性分析。但由于微生物类群数较多&#xff0c;又有不同的分类水平&#xff0…

leetcode-二叉树的镜像-91

题目要求 思路1 1.遍历一遍二叉树&#xff0c;将左边的结点对应创建一个右边的结点 2.用此方法空间复杂度O(n)&#xff0c;并不是最优 思路2 1.将一个结点的左右子树进行交换&#xff0c;如果左子树还有左右结点&#xff0c;就再交换左子树的左右结点&#xff0c;以此递归下去…

Kubernetes:云原生时代的核心引擎

文章目录 一、Kubernetes简介&#xff1a;引领云原生潮流二、K8s的核心特性&#xff1a;自动化与智能化三、K8s的实践应用&#xff1a;打造高效云原生应用架构四、K8s的挑战与应对&#xff1a;安全与性能并重五、K8s的未来展望&#xff1a;无限可能与挑战并存《Kubernetes快速进…

【windows-搭建Ubuntu22LTS】

一、环境要求 1. windows版本要求 至少Windows 10 2020年5月(2004) 版, Windows 10 2019年5月(1903) 版&#xff0c;或者 Windows 10 2019年11月(1909) 版 2. 控制面板开启相关的程序(需要重启) 二、Microsoft store安装unbuntu 下载后直接运行&#xff08;稍微等会&#…

从开发角度理解漏洞成因(01)

文章目录 PHP开发漏洞环境&#xff08;SQL注入&#xff09;生成前端代码生成后端代码数据库写功能调试功能 测试SQL注入漏洞字符型注入布尔盲注 PHP开发漏洞环境&#xff08;SQL注入&#xff09; 持续更新中… 文章中代码资源已上传资源&#xff0c;如需要打包好的请点击PHPM…

亿道三防onerugged|工业车载电脑在港口正面吊上的应用

港口正面吊是港口作业中至关重要的设备&#xff0c;它承担着装卸集装箱等重要任务。作为专业人员&#xff0c;我深知港口作业的复杂性和挑战性。在这方面&#xff0c;亿道三防onerugged系列的工业车载电脑为港口正面吊的应用提供了一种创新的解决方案。 首先&#xff0c;工业车…