签名的实现功能
我们要实现签名:
1.我们首先要鼠标按下,移动,抬起。经过这三个步骤。
我们可以实现一笔或者连笔。
按下的时候我们需要移动画笔,可以使用 moveTo 来移动画笔。
e.pageX,e.pageY来获取坐标位置
移动的时候我们进行绘制
ctx.lineTo(e.pageX,e.pageY)
ctx.stroke()
通过开关flag来判断是否绘制
2.我们可以调整画笔的粗细
3.当我们写错的时候,可以撤回上一步
4.重置整个画板
5.点击保存的时候,可以生成一张图片
6.base64转化为file
实现签名
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<style>
*{
padding: 0;
margin: 0;
}
#canvas {
border: 2px dotted #ccc;
background-repeat: no-repeat;
background-size: 80px;
}
style>
head>
<body>
<div class="con-box">
<canvas id="canvas" width="600px" height="400px">canvas>
<button id="save-btn" onclick="saveHandler">保存button>
<button id="reset-btn" onclick="resetHandler">重置button>
div>
body>
<script>
const canvas=document.getElementById('canvas')
const ctx= canvas.getContext('2d')
ctx.strokeStyle='#000'
let flag= false
canvas.addEventListener('mousedown',e=>{
console.log('按下',e.pageX,e.pageY)
flag=true
ctx.moveTo(e.pageX,e.pageY)
})
canvas.addEventListener('mousemove',e=>{
console.log('移动')
if(flag){
ctx.lineTo(e.pageX,e.pageY)
ctx.stroke()
}
})
canvas.addEventListener('mouseup',e=>{
console.log('抬起')
flag=false
})
script>
html>
鼠标移入canvas就会触发事件
通过上面的图,我们发现了一个点。
那就是鼠标移入canvas所在的区域。
就会触发移动事件的代码。
这是为什么呢?
因为我们在移入的时候注册了事件,因此就会触发。
现在我们需要优化一下:将移动事件,抬起事件放在按下事件里面
同时,当鼠标抬起的时候,移除移动事件和抬起事件。【不移除按下事件】
这里可能有的小伙伴会问?
为什么抬起的时候不移除按下事件。
因为:代码从上往下执行,当我们移除抬起事件后,我们只能绘画一次了。
当我们移除事件时,我们就不需要开关 flag 了。
删除flag的相关代码
<script>
const canvas=document.getElementById('canvas')
const ctx= canvas.getContext('2d')
ctx.strokeStyle='#000'
canvas.addEventListener('mousedown',mousedownFun)
function mousedownFun(e){
console.log('按下',e.pageX,e.pageY)
ctx.moveTo(e.pageX,e.pageY)
canvas.addEventListener('mousemove',mousemoveFun)
canvas.addEventListener('mouseup',mouseupFun)
}
function mousemoveFun(e){
console.log('移动')
ctx.lineTo(e.pageX,e.pageY)
ctx.stroke()
}
function mouseupFun(e){
console.log('抬起')
canvas.removeEventListener('mousemove', mousemoveFun)
canvas.removeEventListener('mouseup', mouseupFun)
}
script>
发现bug-鼠标不按下也可以绘制笔画
我们发现鼠标移出canvas所在区域后。
然后在移入进来,鼠标仍然可以进行绘制。(此时鼠标已经是松开了)
这很明显是一个bug。这个bug产生的原因在于:
鼠标移出canvas所在区域后没有移出移动事件
canvas.addEventListener('mouseout',e=>{
canvas.removeEventListener('mousemove', mousemoveFun)
})
如何设置画笔的粗细
我们想要调整画笔的粗细。
需要使用 ctx.lineWidth 属性来设置画笔的大小默认是1。
我们用 <input type="range" class="range" min="1" max="30" value="1" id="range">
来调整画笔。
因为我们我们调整画笔后,线条的大小就会发生改变。
因此我们在每次按下的时候都需要开始本次绘画。
抬起的时候结束本次绘画,
这样才能让不影响上一次画笔的大小。
核心的代码
"range" class="range" min="1" max="30" value="1" id="range">
let range = document.querySelector("#range");
const ctx= canvas.getContext('2d')
function mousedownFun(e){
console.log('按下',e.pageX,e.pageY)
ctx.beginPath();
ctx.lineWidth = range.value || 1
}
function mouseupFun(e){
ctx.closePath();
console.log('抬起')
}
撤回上一步
1. 先声明一个数组. let historyArr=[]
按下的时候记录当前笔画起始点的特征(颜色 粗细 位置)
currentPath = {
color: ctx.strokeStyle,
width: ctx.lineWidth,
points: [{ x: e.offsetX, y: e.offsetY }]
}
2.按下移动的时候记录每一个坐标点[点连成线]
currentPath.points.push({ x: e.offsetX, y: e.offsetY });
3.鼠标抬起的时候说明完成了一笔(连笔)
historyArr.push(currentPath);
4.点击撤销按钮的时候删除最后一笔
5.然后重新绘制之前存储的画笔
<button id="revoke">撤销button>
let historyArr = []
let currentPath = null;
let revoke=document.querySelector("#revoke");
function mousedownFun(e){
ctx.beginPath();
ctx.lineWidth = range.value || 1
ctx.moveTo(e.pageX,e.pageY)
currentPath = {
color: ctx.strokeStyle,
width: ctx.lineWidth,
points: [{ x: e.offsetX, y: e.offsetY }]
}
}
function mousemoveFun(e){
ctx.lineTo(e.pageX,e.pageY)
currentPath.points.push({ x: e.offsetX, y: e.offsetY });
ctx.stroke()
}
function mouseupFun(e){
historyArr.push(currentPath);
ctx.closePath();
}
revoke.addEventListener('click', e => {
if (historyArr.length === 0) return;
historyArr.pop()
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPaths(historyArr);
});
function drawPaths(paths) {
paths.forEach(path => {
ctx.beginPath();
ctx.strokeStyle = path.color;
ctx.lineWidth = path.width;
ctx.moveTo(path.points[0].x, path.points[0].y);
console.log('path',path)
path.points.slice(1).forEach(point => {
ctx.lineTo(point.x, point.y);
});
ctx.stroke();
});
}
重置整个画布
reset.addEventListener('click',e=>{
ctx.clearRect(0, 0, canvas.width, canvas.height);
})
ps:清空画布的主要运用了ctx.clearRect这个方法
保存
保存图片主要是通过 canvas.toDataURL 生成的是base64
然后通过a标签进行下载
saveBtn.addEventListener('click',()=>{
let imgURL = canvas.toDataURL({format: "image/png", quality:1, width:600, height:400});
let link = document.createElement('a');
link.download = "tupian";
link.href = imgURL;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
})
生成file文件发送给后端
function base64changeFile (urlData, fileName) {
const arr = urlData.split(',')
const mimeType = arr[0].match(/:(.*?);/)[1]
console.log('类型',mimeType)
const bytes = atob(arr[1])
let n = bytes.length
const fileFormat = new Uint8Array(n)
while (n--) {
fileFormat[n] = bytes.charCodeAt(n)
}
return new File([fileFormat], fileName, { type: mimeType })
}
fileBtn.addEventListener('click',()=>{
let imgURL = canvas.toDataURL({format: "image/png", quality:1, width:600, height:400});
let file = base64changeFile(imgURL,'qianMing')
console.log('file',file)
})
全部代码
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<style>
*{
padding: 0;
margin: 0;
}
#canvas {
border: 2px dotted #ccc;
}
style>
head>
<body>
<div class="con-box">
<canvas id="canvas" width="600px" height="400px">canvas>
<input type="range" class="range" min="1" max="30" value="1" id="range">
<button id="revoke">撤销button>
<button id="save-btn">保存button>
<button id="file">转化为filebutton>
<button id="reset" >重置button>
div>
body>
<script>
const canvas=document.getElementById('canvas')
let range = document.querySelector("#range");
let revoke=document.querySelector("#revoke");
let reset=document.querySelector("#reset");
let saveBtn=document.querySelector("#save-btn");
let fileBtn=document.querySelector("#file");
const ctx= canvas.getContext('2d')
ctx.strokeStyle='#000'
let historyArr = []
let currentPath = null;
canvas.addEventListener('mousedown',mousedownFun)
function mousedownFun(e){
console.log('按下',e.pageX,e.pageY)
ctx.beginPath();
ctx.lineWidth = range.value || 1
ctx.moveTo(e.pageX,e.pageY)
currentPath = {
color: ctx.strokeStyle,
width: ctx.lineWidth,
points: [{ x: e.offsetX, y: e.offsetY }]
}
canvas.addEventListener('mousemove',mousemoveFun)
canvas.addEventListener('mouseup',mouseupFun)
}
function mousemoveFun(e){
console.log('移动')
ctx.lineTo(e.pageX,e.pageY)
currentPath.points.push({ x: e.offsetX, y: e.offsetY });
ctx.stroke()
}
function mouseupFun(e){
historyArr.push(currentPath);
console.log('historyArr',historyArr)
ctx.closePath();
console.log('抬起')
canvas.removeEventListener('mousemove', mousemoveFun)
canvas.removeEventListener('mouseup', mouseupFun)
}
canvas.addEventListener('mouseout',e=>{
canvas.removeEventListener('mousemove', mousemoveFun)
})
revoke.addEventListener('click', e => {
if (historyArr.length === 0) return;
historyArr.pop()
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPaths(historyArr);
});
reset.addEventListener('click',e=>{
ctx.clearRect(0, 0, canvas.width, canvas.height);
})
saveBtn.addEventListener('click',()=>{
let imgURL = canvas.toDataURL({format: "image/png", quality:1, width:600, height:400});
console.log('imgURL',imgURL)
let link = document.createElement('a');
link.download = "tupian";
link.href = imgURL;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
})
function drawPaths(paths) {
console.log(11,paths)
paths.forEach(path => {
ctx.beginPath();
ctx.strokeStyle = path.color;
ctx.lineWidth = path.width;
ctx.moveTo(path.points[0].x, path.points[0].y);
console.log('path',path)
path.points.slice(1).forEach(point => {
ctx.lineTo(point.x, point.y);
});
ctx.stroke();
});
}
function base64changeFile (urlData, fileName) {
const arr = urlData.split(',')
const mimeType = arr[0].match(/:(.*?);/)[1]
console.log('类型',mimeType)
const bytes = atob(arr[1])
let n = bytes.length
const fileFormat = new Uint8Array(n)
while (n--) {
fileFormat[n] = bytes.charCodeAt(n)
}
return new File([fileFormat], fileName, { type: mimeType })
}
fileBtn.addEventListener('click',()=>{
let imgURL = canvas.toDataURL({format: "image/png", quality:1, width:600, height:400});
let file = base64changeFile(imgURL,'qianMing')
console.log('file',file)
})
script>
html>