如图所示:前端实现H5+canvas手写板电子签名:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>签名</title>
<!-- 引入 Vue 和 Vant 的 JS 文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vant@2.9/lib/index.css" />
<!-- 引入 Vue 和 Vant 的 JS 文件 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vant@2.9/lib/vant.min.js"></script>
<style>
body,
html {
margin: 0 auto;
width: 100%;
padding: 0;
height: 100%;
}
.appview{
display: flex;
flex: 1;
margin: 0 auto;
width: 95%;
padding: 10px 0px;
}
#app{
position: relative;
text-align: center;
width: 100%;
margin: 0 auto;
/* margin-top: 10px; */
}
#canvas {
border:1px solid #333;
}
#canvas-btn {
display: block;
margin: 0;
/* padding: 0.3rem; */
}
#clear_btn {
width:40px;
/* background: #999; */
margin: auto;
text-align: center;
line-height: 1rem;
margin-top: 0.27rem;
height: 86px;
margin-top: 80px;
line-height: 86px;
color: #fff;
border-radius:5px;
clear: both;
}
#save_btn {
width:43px;
/* background: #2b85e4; */
margin: auto;
text-align: center;
line-height: 1rem;
height: 86px;
line-height: 86px;
margin-top: 0.27rem;
color: #fff;
border-radius:5px;
clear: both;
}
.span{
-moz-transform: rotate(90deg);
-webkit-transform: rotate(90deg);
display: block;
position: absolute;
left: 10px;
filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3)
}
</style>
</head>
<body>
<div id="app">
<div class="appview">
<div id="canvas-btn">
<!-- <div id="clear_btn" class="op_btn">
</div> -->
<img src="./img/btn1.png" id="clear_btn" class="op_btn">
<!-- <div id="save_btn" class="save_btn">
<span class="span">完成签名</span> -->
<img src="./img/btn2.png" id="save_btn" class="save_btn">
<!-- </div> -->
<div class="cleaerfix"></div>
</div>
<canvas id='canvas'></canvas>
</div>
</div>
<script src="./js/config.js"></script>
<script type="text/javascript" src="./js/jquery.min.js"></script>
<script type="text/javascript" src="./js/flexible.debug.js"></script>
<script type="text/javascript" src="./js/zepto.js"></script>
<script type="text/javascript" src="./js/touch.js"></script>
<script type="text/javascript" src="./js/flexible.debug.js"></script>
<script type="text/javascript" src="./js/flexible_css.debug.js"></script>
<script>
//获取页面尺寸
var canvasWidth = document.body.clientWidth;
var canvasHeight = 600;
//声明canvas
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
//设置canvas尺寸
canvas.width = canvasWidth * 0.8;
canvas.height = canvasHeight;
//画笔颜色
var strokeColor = "#333";
//鼠标
isMouseDown = false;
//上一次绘制的的坐标
var lastLoc = { x: 0, y: 0 };
//初始记录事件
var lastTimestamp = 0;
//上一次线条宽度
var lastLineWidth = -1;
//var
var maxV = 10;
var minV = 0.1;
var maxLineWidth = 5;
var minLineWidth = 1;
//点击色块切换画笔颜色
$(".colorBtn").on("click", function (e) {
$(".colorBtn").removeClass('colorBtnBorder');
$(this).addClass("colorBtnBorder");
strokeColor = $(this).css("");
})
//清除
$('#clear_btn').on('click', function (e) {
context.clearRect(0, 0, canvasWidth, canvasHeight);
});
//保存画图
$('#save_btn').on('click', function (e) {
var image = canvasToImage(canvas);
console.log("image", image)
console.log(image);
});
//获取canvas 坐标 x,y 分别代表相对window内的xy
function windowToCanvas(x, y) {
//canvas提供的方法返回canvas 距 他外围包围盒子的距离left,top值
var bbox = canvas.getBoundingClientRect();
//返回的就是canvas 内的坐标值
return { x: Math.round(x - bbox.left), y: Math.round(y - bbox.top) }
}
//封装 事件
function beginStroke(point) {
isMouseDown = true;
//第一次用户画的坐标初始值
lastLoc = windowToCanvas(point.x, point.y);
//获取首次点击鼠标 事件戳
lastTimestamp = new Date().getTime();
}
function endStroke() {
isMouseDown = false;
}
function moveStroke(point) {
//开始绘制直线
var curLoc = windowToCanvas(point.x, point.y);
//路程
var s = calcDistance(curLoc, lastLoc);
//结束时间
var curTimestamp = new Date().getTime();
//时间差
var t = curTimestamp - lastTimestamp;
//绘制线条粗细
var lineWidth = calcLineWidth(t, s);
//绘制
context.beginPath();
context.moveTo(lastLoc.x, lastLoc.y);
context.lineTo(curLoc.x, curLoc.y);
context.strokeStyle = strokeColor;
context.lineWidth = lineWidth;
context.lineCap = "round";
context.lineJoin = "round";
context.stroke();
//给lastLoc赋值维护
lastLoc = curLoc;
//时间更新
lastTimestamp = curTimestamp;
lastLineWidth = lineWidth;
}
//pc鼠标事件
canvas.onmousedown = function (e) {
e.preventDefault();
beginStroke({ x: e.clientX, y: e.clientY });
}
canvas.onmouseup = function (e) {
e.preventDefault();
endStroke();
}
canvas.onmouseout = function (e) {
e.preventDefault();
endStroke();
}
canvas.onmousemove = function (e) {
e.preventDefault();
if (isMouseDown) {
moveStroke({ x: e.clientX, y: e.clientY });
}
}
//移动端
canvas.addEventListener("touchstart", function (e) {
e.preventDefault();
touch = e.touches[0]; //限制一根手指触碰屏幕
beginStroke({ x: touch.pageX, y: touch.pageY });
});
canvas.addEventListener("touchend", function (e) {
e.preventDefault();
endStroke();
});
canvas.addEventListener("touchmove", function (e) {
e.preventDefault();
if (isMouseDown) {
touch = e.touches[0];
moveStroke({ x: touch.pageX, y: touch.pageY });
}
});
//速度 = 路程 / 时间 用来计算书写速度来改变线条粗细
function calcDistance(loc1, loc2) {
//返回 数的平方根
return Math.sqrt((loc1.x - loc2.x) * (loc1.x - loc2.x) + (loc1.y - loc2.y) * (loc1.y - loc2.y));
}
//线条宽度
function calcLineWidth(t, s) {
var v = s / t;
var resultLineWidth;
if (v <= minV) {
resultLineWidth = maxLineWidth;
} else if (v >= maxV) {
resultLineWidth = minLineWidth;
} else {
resultLineWidth = maxLineWidth - (v - minV) / (maxV - minV) * (maxLineWidth - minLineWidth);
}
if (lastLineWidth == -1) {
return resultLineWidth;
} else {
return lastLineWidth * 2 / 3 + resultLineWidth * 1 / 3;
}
}
// 将canvas转换成画布
function canvasToImage(canvas) {
var image = new Image();
console.log("----", canvas.toDataURL("image/png"));
var that = this;
var formData = new FormData();
formData.append("file", canvas.toDataURL("image/png"));
// return;
// 请求登录接口
$.ajax({
url: config.upBase64Pic+"?appid=7",
async: false,
type: "POST",
data: formData,
dataType: "json",
processData: false,
contentType: false,
// dataType: 'json',
success: function (data) {
console.log("上传==》", data)
if (data.code == 200) {
localStorage.setItem("fast", data.fast);
localStorage.setItem("local", data.local);
window.location.href="./agreement.html"
} else {
vant.Toast({
message: "上传异常~",
forbidClick: true,
});
}
},
error: function (data, res, res2) {
console.log(data, res, res2)
vant.Toast({
message: '网络忙',
forbidClick: true,
});
}
});
image.src = canvas.toDataURL("image/png");
// console.log("")
return image;
}
</script>
</body>
</html>