JavaScript使用canvas实现手写签名功能
预览效果
如果不想阅读文章,可直接查看源码
先实现基本需求(能签名即可)
整理思路
准备一个canvas画布,得到context对象
指定画笔的样式
监听鼠标 / 手指的移动,得到每一次移动在画布上的坐标点,记录下来
将这些点绘制到画布上形成线条
<canvas width="600" height="400" id="canvas" style="background-color: #ddd;"></canvas>
为了方便调试,我们本次仅演示pc端的操作。移动端思路是一样的,只不过监听的API不同。
常见的操作方式是:当鼠标左键按下的时候在画布上移动鼠标,就可以绘制。没有按下鼠标时,不管它。
注释很重要,我尽量写得很详细
window.onload = function () { // 默认鼠标是没有按下的 let isDown = false; // 记录上一次鼠标的位置 let lastX = 0; // x轴 let lastY = 0; // y轴 // 获取canvas const canvas = document.getElementById("canvas"); // 获取canvas的上下文 const ctx = canvas.getContext("2d"); // 定义线条的宽度,即画笔的粗细 ctx.lineWidth = 3; // 定义画笔的颜色 ctx.strokeStyle = "#000"; /** * 定义绘制方法 * 线条其实是由两个点连起来的一个线段 * 一个又一个的小线段,连起来就组成了一个线条 * 在画布上绘制线条,主要用到的三个核心方法 * moveTo: 是 Canvas 2D API 将一个新的子路径的起始点移动到 (x,y) 坐标的法。 * lineTo: 是 Canvas 2D API 使用直线连接子路径的终点到 x,y 坐标的法。 * 当然,定义了起点和终点还不够,还需要手动调用开始绘制这个路径 * startX 和 startY 一起组成了起点的坐标 * endX 和 endY 一起组成了线段终点的坐标 */ function draw(startX, startY, endX, endY) { // 起点 ctx.moveTo(startX, startY); // 终点 ctx.lineTo(endX, endY); // 调用 stroke,即可看到绘制的线条 ctx.stroke(); } // 监听鼠标按下,得到按下时鼠标在画布上的坐标 canvas.addEventListener("mousedown", ({ x, y }) => { isDown = true; // 按下时的点作为起点 lastX = x; lastY = y; // 创建一个新的路径 ctx.beginPath(); }, false); // 监听鼠标移动 canvas.addEventListener("mousemove", ({ x, y }) => { // 没有按下就不管 if (!isDown) return; // 调用绘制方法 draw(lastX, lastY, x, y); // 把当前移动时的坐标作为下一次的绘制路径的起点 lastX = x; lastY = y; }, false); // 监听鼠标抬起 canvas.addEventListener("mouseup", () => { isDown = false; // 关闭路径 ctx.closePath(); }, false); // 监听鼠标移出 canvas.addEventListener("mouseleave", () => { // 移出canvas范围,也认为是鼠标抬起了,再移入需要重新按下鼠标,避免移出之后再抬起鼠标,重新进入画笔还能继续画的问题 isDown = false; ctx.closePath(); }, false); };
以上代码就实现了最基本的签名功能。
将canvas导出为图片
这个功能比较简单,思路都在注释里了
// 使用canvas的toDataURL()方法,将画布内容转换为base64格式的图片数据: let imgData = canvas.toDataURL('image/png'); // 创建下载链接 let link = document.createElement('a'); link.download = 'picture.png'; link.href = imgData; // 触发点击 link.click(); // 移除元素 document.body.removeChild(link);
撤销和重写功能
整理思路
要实现撤销笔画回到上一步,就要知道上一步画了什么,就是要记录下来,我们可以用一个数组,把每次鼠标移动时得到的坐标放进去。
通过基础功能我们知道了,画布上签名,是由多个线条组成的,而线条是由很多个点组成的。那我们撤销的时候,是撤销一条线,即一个笔画,而不是一个点。
那么,怎么知道哪些点是属于一个笔画的呢,就是要给这些点分组,一个笔画为一组。我们规定,从鼠标按下到鼠标抬起,这之间移动时产生的所有点为一组,即一个笔画。用代码表示,就是有多个数组,所以我们定一个二维数组来保存所有的点。
改写一下前面的代码
window.onload = function () { // 默认鼠标是没有按下的 let isDown = false; // // 记录上一次鼠标的位置 // let lastX = 0; // x轴 // let lastY = 0; // y轴 // 这次要用数组来记录 let points = []; // 这是一个笔画的点 let allPonits = []; // 这是所有笔画的点 // 获取canvas元素 const canvas = document.getElementById("canvas"); // 获取canvas的上下文 const ctx = canvas.getContext("2d"); // 定义线条的宽度,即画笔的粗细 ctx.lineWidth = 3; // 定义画笔的颜色 ctx.strokeStyle = "#000"; function draw(startX, startY, endX, endY) { // 起点 ctx.moveTo(startX, startY); // 终点 ctx.lineTo(endX, endY); // 调用 stroke,即可看到绘制的线条 ctx.stroke(); } // 监听鼠标按下,得到按下时鼠标在画布上的坐标 canvas.addEventListener("mousedown", ({ x, y }) => { isDown = true; // lastX = x; // lastY = y; // 保存当前坐标作为起点 points.push({ x, y }); // 创建一个新的路径 ctx.beginPath(); }, false); // 监听鼠标移动 canvas.addEventListener("mousemove", ({ x, y }) => { // 没有按下就不管 if (!isDown) return; // 调用绘制方法 // draw(lastX, lastY, x, y); // 把当前移动时的坐标作为下一次的绘制路径的起点 // lastX = x; // lastY = y; // 每次都取最后一个点,作为绘制的起点 const lastPoint = points.at(-1); draw(lastPoint.x, lastPoint.y, x, y); // 把当前的点保存起来,又作为下一次绘制的起点 points.push({ x, y }); }, false); // 监听鼠标抬起 canvas.addEventListener("mouseup", (e) => { isDown = false; // 关闭路径 ctx.closePath(); // 鼠标抬起,说明当前这一笔就结束了,把这一笔的所有点的数组放到总的里面 allPonits.push(points); // 清空这一笔画,为下一笔画做准备 points = []; }, false); // 监听鼠标移出 canvas.addEventListener("mouseleave", () => { // 移出canvas范围,也认为是鼠标抬起了,再移入需要重新按下鼠标,避免移出之后再抬起鼠标,重新进入画笔还能继续画的问题 isDown = false; // 关闭画笔 ctx.closePath(); // 如果是先抬起鼠标再移出,那么points里面为空,就不保存了 // 如果是先移出范围,移出时就保存,这样也不会触发上面的监听鼠标抬起事件,也不会push。 if (points.length) { allPonits.push(points); } // 移出时也清空,因为无法判断是先抬起还是先移出的。 points = []; }, false); };
在页面上加两个按钮
<div> <button id="prev">上一步</button> <button id="reset">重写</button> </div>
const prev = document.getElementById("prev"); const reset = document.getElementById("reset"); // 清空画布 function resetPath() { ctx.clearRect(0, 0, canvas.width, canvas.height); } // 上一步 prev.addEventListener("click", (e) => { // canvas本身不会记录用户的每一步操作 // 要回到上一步,只能一次性清空所有的 resetPath(); // 删除最后一个笔画 allPonits.pop(); // 遍历所有的笔画并重新绘制 // allPoints 是个二维数组 allPonits.forEach((ps) => { ps.forEach((item, index) => { // 下一个坐标点 let next = ps[index + 1]; if (next) { // 有下一个点才执行,否则到最后一个会报错 // 开始重新绘制 ctx.beginPath(); draw(item.x, item.y, next.x, next.y); ctx.closePath(); } }); }); }); // 重写 reset.addEventListener("click", () => { // 点击重写时清空画布,并清空所有的点 resetPath(); allPonits = []; }, false);
到这里,我们就完成了canvas手写签名,并且实现了撤销和重写,以及导出为图片的功能。
以上就是JavaScript使用canvas实现手写签名功能的详细内容,更多关于JavaScript canvas手写签名的资料请关注脚本之家其它相关文章!
相关文章
javascript 支持链式调用的异步调用框架Async.Operation
javascript 支持链式调用的异步调用框架Async.Operation2009-08-08js中innerText/textContent和innerHTML与target和currentTarget的区别
今天小编就为大家分享一篇关于js中innerText/textContent和innerHTML与target和currentTarget的区别,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧2019-01-01
最新评论