移动端在开发的时候,可能会涉及到手写签名(电子签名)那些,在前端的思路是使用canvas 来签名,导出成图片进行保存。

仅供参考和学习。

vue版本: https://blog.csdn.net/qq_33270001/article/details/102855964

注意:在移动端使用的时候, 写竖的时候, 页面会被往下拉, 手写板动了, 写字不顺畅. 建议在移动端的touchmove事件里, 加一行防止页的滑动事件, 代码是: event.preventDefault();

<!DOCTYPE html>
<!-- saved from url=(0056)http://hao2013.cn/canvas-special-master/brush/index.html -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>签名板(支持移动端)</title>
</head>
<style type="text/css">
*{margin: 0;padding: 0;}
.canvas {
/*width: 100%;*/
display: block;
border: 1px solid red;
}
#clear,
#clear1,
#save {
margin: 0 auto;
display: inline-block;
padding: 5px 10px;
width: 50px;
height: 40px;
line-height: 40px;
border: 1px solid #eee;
background: #e1e1e1;
border-radius: 10px;
text-align: center;
margin: 20px auto;
cursor: pointer;
}
</style>
<body data-ext-version="1.4.2">
<canvas id="canvas" width="600" height="600">您的浏览器不支持canvas技术,请升级浏览器!</canvas>
<div style="text-align: center">
<span id="clear">清空</span>
<span id="save">保存</span>
</div>
</body>
<script type="text/javascript">
function WriteFont(id, options) {
var self = this;
this.canvas = document.getElementById(id);
var obj = {
canvas: this.canvas,
context: this.canvas.getContext("2d"),
isWrite: false, //是否开始
lastWriteTime: -1,
lastWriteSpeed: 0,
lastWriteWidth: 0,
canvasWidth: 600, //canvas宽高
canvasHeight: 600,
isShowBorder: true, //是否显示网格
bgColor: '#fff', //背景色
borderWidth: 2, // 网格线宽度
borderColor: "#fff", //网格颜色
lastPoint: {}, //
writeWidth: 2, //基础轨迹宽度
maxWriteWidth: 30, // 写字模式最大线宽
minWriteWidth: 1, // 写字模式最小线宽
writeColor: '#000', // 轨迹颜色
isWriteName:false //签名模式
}for(var name in options) {
obj[name] = options[name];
}/**
* 轨迹宽度
*/
this.setLineWidth = function() {
var nowTime = new Date().getTime();
var diffTime = nowTime - obj.lastWriteTime;
obj.lastWriteTime = nowTime;
var returnNum = obj.minWriteWidth + (obj.maxWriteWidth - obj.minWriteWidth) * diffTime / 30;
if(returnNum < obj.minWriteWidth) {
returnNum = obj.minWriteWidth;
} else if(returnNum > obj.maxWriteWidth) {
returnNum = obj.maxWriteWidth;
}returnNum = returnNum.toFixed(2);
//写字模式和签名模式
if(obj.isWriteName){
obj.context.lineWidth = obj.writeWidth;
}else{
obj.context.lineWidth = obj.lastWriteWidth = obj.lastWriteWidth / 4 * 3 + returnNum / 4;
}
}/**
* 绘制轨迹
*/
this.writing = function(point) {
obj.context.beginPath();
obj.context.moveTo(obj.lastPoint.x, obj.lastPoint.y);
obj.context.lineTo(point.x, point.y);
self.setLineWidth();
obj.context.stroke();
obj.lastPoint = point;
obj.context.closePath();
}/**
* 轨迹样式
*/
this.writeContextStyle = function() {
obj.context.beginPath();
obj.context.strokeStyle = obj.writeColor;
obj.context.lineCap = 'round';
obj.context.lineJoin = "round";
}/**
* 写开始
*/
this.writeBegin = function(point) {
obj.isWrite = true;
obj.lastWriteTime = new Date().getTime();
obj.lastPoint = point;
self.writeContextStyle();
}/**
* 写结束
*/
this.writeEnd = function() {
obj.isWrite = false;
}/**
* 清空画板
*/
this.canvasClear = function() {
obj.context.save();
obj.context.strokeStyle = '#fff';
obj.context.clearRect(0, 0, obj.canvasWidth, obj.canvasHeight);
if(obj.isShowBorder && !obj.isWriteName) {
obj.context.beginPath();
var size = obj.borderWidth / 2;
//画外面的框
obj.context.moveTo(size, size);
obj.context.lineTo(obj.canvasWidth - size, size);
obj.context.lineTo(obj.canvasWidth - size, obj.canvasHeight - size);
obj.context.lineTo(size, obj.canvasHeight - size);
obj.context.closePath();
obj.context.lineWidth = obj.borderWidth;
obj.context.strokeStyle = obj.borderColor;
obj.context.stroke();
//画里面的框
obj.context.moveTo(0, 0);
obj.context.lineTo(obj.canvasWidth, obj.canvasHeight);
obj.context.lineTo(obj.canvasWidth, obj.canvasHeight / 2);
obj.context.lineTo(obj.canvasWidth, obj.canvasHeight / 2);
obj.context.lineTo(0, obj.canvasHeight / 2);
obj.context.lineTo(0, obj.canvasHeight);
obj.context.lineTo(obj.canvasWidth, 0);
obj.context.lineTo(obj.canvasWidth / 2, 0);
obj.context.lineTo(obj.canvasWidth / 2, obj.canvasHeight);
obj.context.stroke();}
obj.context.restore();
}/**
* 保存图片 格式base64
*/
this.saveAsImg = function() {
var image = new Image();
image.src = this.canvas.toDataURL("image/png");
if(image.src == this.emptyCanvas) {
alert('请先书写')
} else {
console.log('提交的内容===>', image.src)
}
};/**
* 初始化画板
*/
this.canvasInit = function() {
this.canvas.width = obj.canvasWidth;
this.canvas.height = obj.canvasHeight;
this.emptyCanvas = this.canvas.toDataURL("image/png");
}/**======================事件绑定===========================**/this.canvas.addEventListener('mousedown', function(e) {
var point = {
x: e.offsetX || e.clientX,
y: e.offsetY || e.clientY
};
self.writeBegin(point);
});this.canvas.addEventListener('mouseup', function(e) {
var point = {
x: e.offsetX,
y: e.offsetY
};
self.writeEnd(point);
});this.canvas.addEventListener('mouseleave', function(e) {
var point = {
x: e.offsetX,
y: e.offsetY
};
self.writeEnd(point);
});this.canvas.addEventListener('mousemove', function(e) {
if(obj.isWrite) {
var point = {
x: e.offsetX,
y: e.offsetY
};self.writing(point);
}
});//移动端
this.canvas.addEventListener('touchstart', function(e) {
var touch = e.targetTouches[0];
var point = {
x: touch.pageX || touch.clientX,
y: touch.pageY || touch.clientY
};
self.writeBegin(point);
});
this.canvas.addEventListener('touchend', function(e) {
var touch = e.changedTouches[0];
var point = {
x: touch.pageX,
y: touch.pageY
};
self.writeEnd(point);
});
this.canvas.addEventListener('touchmove', function(e) {
var touch = e.targetTouches[0];
var point = {
x: touch.pageX,
y: touch.pageY
};
self.writeEnd(point);
});
this.canvas.addEventListener('touchmove', function(e) {
var touch = e.targetTouches[0];
var point = {
x: touch.pageX,
y: touch.pageY
};
self.writing(point);
});this.canvasInit();
this.canvasClear();this.option = obj;
obj.control = {
clearCanvas: self.canvasClear
};
}/**
* 初始化调用
* 设置参数
*/
var writeCanvas = new WriteFont('canvas', {
borderWidth: 10,
writeWidth:3,
borderColor: '#ff6666',
isWriteName:true //签名模式
});document.getElementById('clear').onclick = function() {
writeCanvas.option.control.clearCanvas();
};document.getElementById('save').onclick = function() {
writeCanvas.saveAsImg()
};
</script>
</html>

效果图如下:

保存的时候是 base 64的图片

附上demo和源码

附件地址: http://hao2013.cn/zb_users/upload/2018/07/201807291807177677029.zip

参考:http://hao2013.cn/?id=41

web前端 原生js签名板(电子签名)写字板 canvas 截图相关推荐

  1. 好程序员web前端分享js剪切板Clipboard.js 使用

    好程序员web前端分享js剪切板Clipboard.js 使用,clipboard.js是一个用来设置剪切板的库,小巧无依赖,但用法有点诡异,必须依赖一个DOM元素. 必须要与一个DOM元素相关联,并 ...

  2. 好程序员web前端分享JS引擎的执行机制

    好程序员web前端分享JS引擎的执行机制,请先着重牢记两点!JS是单线程语言. JS的EventLoop是JS的执行机制.深入了解JS的执行,就等于深入了解JS里的eventloop. 1.灵魂三问: ...

  3. Web前端-Vue.js必备框架(一)

    Web前端-Vue.js必备框架(一) <!DOCTYPE html> <html lang="en"> <head><meta char ...

  4. js 点击闭包_【新年跳槽必备】2020最新(前端原生JS专题)面试题 速领!

    最近我把每周更新的面试题 发在我们的学习群里 大家似乎都很高冷哇 难道是默默的做题去了没说话 每期面试题都是Richard老师认真准备的 真的希望能帮到大家哦 本期是前端原生JS专题 A前端原生JS专 ...

  5. 手机端移动端的前端原生js裁剪图片上传

    手机端移动端的前端原生js裁剪图片上传 选择头像时裁剪上传,确保图片是个正方形,不会出现压扁拉伸的现象 效果图 原理很简单,其实就是用canvas截图出来而已,只是要对比例做一下处理. <!-- ...

  6. Web前端,JS基础之ATM取款机案例

    前言 持续学习总结输出中,今天分享的是Web前端,JS基础之ATM取款机案例 需求 用户在ATM取款时可以选择存钱.取钱.查看余额和退出功能 分析 1.循环的时候,需要反复提示输入框,所以提示框写要到 ...

  7. Web前端-Vue.js必备框架(二)

    Web前端-Vue.js必备框架(二) vue调式工具vue-devtools 过滤器:vue.js允许你自定义过滤器,可被用作一些常见的文本格式化. mustache插值和v-bind表达式. vu ...

  8. web前端面试 js部分

    1/如何用原生js给一个按钮绑定过两个click事件 var obtn = document.getElementById('btn'); obtn.addEventListener('click', ...

  9. 前端原生js实现图片轮播效果,超级简单,备注详细

    原生js实现简单轮播图,效果如下 纯生js实现轮播图 链接: link. 图片: 我们可以通过左右两边的箭头来播放图片,在我们的鼠标放在图片上时,自动播放结束,转化为手动播放,可以通过小圆点来点击切换 ...

  10. web前端培训JS 运行机制的梳理

    展现形式:由于是属于系统梳理型,就没有由浅入深了,而是从头到尾的梳理知识体系, 重点是将关键节点的知识点串联起来,而不是仅仅剖析某一部分知识. 内容是:从浏览器进程,再到浏览器内核运行,再到JS引擎单 ...

最新文章

  1. 把企业分“三只鸟”的发展好比“三个策略”
  2. linux内核模块编译出现找不到include/generated/asm/unistd_32.h” 问题解决
  3. 启用物料账后,有两种物料价格确定方式
  4. 十八个超经典故事 绝对不会后悔
  5. numpy的基本使用2
  6. Python3 正则相关
  7. LED 将为我闪烁: 控帘 j发光二级管
  8. mysql复杂条件判断_MySQL复杂where条件分析
  9. Java中的Random()函数 【转载】
  10. 使用C#的后端Web API:循序渐进教程
  11. 让我们手动计算:深入研究Logistic回归
  12. 人工智能与自动驾驶汽车_自动驾驶汽车中的道德AI
  13. SciTE AMPL配置问题
  14. 机器人手眼标定原理介绍(含详细推导过程)使用Tsai-Lenz算法
  15. python读取同花顺数据_python爬取同花顺数据
  16. android修改shell串口号,[Note] 2021-01-15 Android shell/串口中使用 wpa_cli 连接Wi-Fi
  17. 阿德勒心理学(自我剖析)
  18. 金彩教育:店铺运营怎么看数据
  19. 如何理解图片RGB通道在python(numpy)中的数据构成
  20. [魔方]魔方七步初级教程

热门文章

  1. 最全的Android源码目录结构详解【转】
  2. 制作app软件具体要多少钱?(整合篇)
  3. 树莓派搭建DLNA客户端,使用gmediarender,DLAN render。
  4. FileZilla Server 设置
  5. EditPlus 5.0 中文免费版,不谢拿走
  6. pc套件 无法连接pc CDC Comms Interface
  7. 软件测试——集成测试篇
  8. 网上商城系统源码 B2C电子商务系统源码
  9. 山东大学高频电子线路实验五 混频器实验详解
  10. Android直播APP源码搭建中豪华物特效的实现