第一步,我们先实现简单的绘制,并且在绘制之后将图片保存到本地
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
iptColor = document.getElementById('iptColor'),//画笔颜色
iptSize = document.getElementById('iptSize'),//画笔大小
btnClear = document.getElementById('btnClear'), //清除按钮
btnSave = document.getElementById('btnSave'),//保存按钮
canvasWidth = 800,
canvasHeight = 600;
canvas.setAttribute('width',canvasWidth);
canvas.setAttribute('height',canvasHeight);
iptSize.oninput = function() {
document.querySelector('.txt-size').innerHTML = iptSize.value
}
canvas.addEventListener('mousedown',function(e){
var e = e || window.event;
ctx.lineWidth = iptSize.value;
ctx.strokeStyle = iptColor.value;
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.beginPath();
ctx.moveTo(e.clientX - canvas.offsetLeft,e.clientY - canvas.offsetTop); //线条起始位置
document.onmousemove = function(e) {
var e = e || window.event;
ctx.lineTo(e.clientX - canvas.offsetLeft,e.clientY - canvas.offsetTop);
ctx.stroke();//绘制线条
};
canvas.onmouseup = function() {
document.onmousemove = null;
document.onmouseup = null;
};
})
//清除画布
btnClear.addEventListener('click',function(){
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
})
//保存图片到本地
btnSave.addEventListener('click',function(){
var imgData = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
var filename = '图片.png';
saveFile(imgData,filename)
})
var saveFile = function(data, filename) {
var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
save_link.href = data;
save_link.download = filename;
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
save_link.dispatchEvent(event);
};
这样看起来虽然说好像没问题,但是这一句有一些问题:
e.clientX - canvas.offsetLeft,e.clientY - canvas.offsetTop
如果页面比较高,产生了滚动条,当滚动条向上滑的时候,绘图的位置就不准确了,示例如:https://codepen.io/jianxiujiu...。
所以此处应该使用:
e.clientX - canvas.getBoundingClientRect().left,e.clientY - canvas.getBoundingClientRect().top
来替换。
第二步,消除涂鸦锯齿的办法
简单的绘制和图片保存完成了,但是在这种情况下,线条会有很明显的锯齿(灵魂画手来了)。
有一个简单粗暴的方法,将线条的边缘进行模糊:
ctx.shadowBlur = 1;
ctx.shadowColor = iptColor.value;
示例:https://codepen.io/jianxiujiu...
加上边缘模糊之后,线条明显柔和了许多,但是还是有一个问题,就是在画笔收笔的地方,线条会变小(灵魂画手又来了)。
在经过搜索查阅之后,发现有一个绘制办法可以降低锯齿的问题。
原理如图:
绘制一个圆点,在下一个圆点之间,绘制一个矩形进行填充。这样绘制出来的线条的衔接会是圆滑的。
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
btnClear = document.getElementById('btnClear'),
btnSave = document.getElementById('btnSave'),
iptColor = document.getElementById('iptColor'),
iptSize = document.getElementById('iptSize'),
canvasWidth = 800,
canvasHeight = 600;
canvas.setAttribute('width',canvasWidth);
canvas.setAttribute('height',canvasHeight);
iptSize.oninput = function() {
document.querySelector('.txt-size').innerHTML = iptSize.value
}
canvas.addEventListener('mousedown',function(e){
var e = e || window.event;
var x1 = e.clientX - canvas.getBoundingClientRect().left,
y1 = e.clientY - canvas.getBoundingClientRect().top;
lineSize = iptSize.value;
lineColor = iptColor.value;
ctx.beginPath();
ctx.fillStyle = lineColor;
ctx.lineCap = "round";
ctx.lineJoin = "round";
ctx.arc(x1, y1, lineSize, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath();
document.onmousemove = function(e) {
var e = e || window.event;
var x2 = e.clientX - canvas.getBoundingClientRect().left,
y2 = e.clientY - canvas.getBoundingClientRect().top;
var asin = lineSize * Math.sin(Math.atan((y2 - y1) / (x2 - x1)));
var acos = lineSize * Math.cos(Math.atan((y2 - y1) / (x2 - x1)));
//分别获取矩形的四个点的xy轴位置
var x3 = x1 + asin;
var y3 = y1 - acos;
var x4 = x1 - asin;
var y4 = y1 + acos;
var x5 = x2 + asin;
var y5 = y2 - acos;
var x6 = x2 - asin;
var y6 = y2 + acos;
ctx.beginPath();
ctx.fillStyle = lineColor;
ctx.arc(x2, y2, lineSize, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.fillStyle = lineColor;
ctx.moveTo(x3, y3);
ctx.lineTo(x5, y5);
ctx.lineTo(x6, y6);
ctx.lineTo(x4, y4);
ctx.fill();
ctx.closePath();
x1 = x2;
y1 = y2;
};
canvas.onmouseup = function() {
document.onmousemove = null;
document.onmouseup = null;
};
})
btnClear.addEventListener('click',function(){
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
})
btnSave.addEventListener('click',function(){
var imgData = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
var filename = '图片.png';
saveFile(imgData,filename)
})
var saveFile = function(data, filename) {
var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
save_link.href = data;
save_link.download = filename;
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
save_link.dispatchEvent(event);
};
DEMO:
https://codepen.io/jianxiujiu...
第三步,添加橡皮擦工具
用这个方法,我们还可以加上橡皮擦。
橡皮擦的原理是,将橡皮绘制的路径覆盖到原来的画笔上。
这里我们将用到canvas的一个属性globalCompositeOperation,详解参照:
http://www.w3school.com.cn/ta...
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
btnClear = document.getElementById('btnClear'),
btnSave = document.getElementById('btnSave'),
btnRestore = document.getElementById('btnRestore'),
iptColor = document.getElementById('iptColor'),
iptSize = document.getElementById('iptSize'),
eraser = document.getElementById('eraser')
pen = document.getElementById('pen'),
canvasWidth = 800,
canvasHeight = 600,
isClear = 0;
canvas.setAttribute('width',canvasWidth);
canvas.setAttribute('height',canvasHeight);
iptSize.oninput = function() {
document.querySelector('.txt-size').innerHTML = iptSize.value
}
eraser.addEventListener('click',function(){
isClear = 1;
this.classList.add('cho');
pen.classList.remove('cho')
})
pen.addEventListener('click',function(){
isClear = 0;
this.classList.add('cho');
eraser.classList.remove('cho')
})
canvas.addEventListener('mousedown',function(e){
var e = e || window.event;
var x1 = e.clientX - canvas.getBoundingClientRect().left,
y1 = e.clientY - canvas.getBoundingClientRect().top;
lineSize = iptSize.value;
if(isClear == 0){
lineColor = iptColor.value;
ctx.beginPath();
ctx.fillStyle = lineColor;
ctx.arc(x1, y1, lineSize, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath();
document.onmousemove = function(e) {
draw();
};
btnClear.classList.remove('dis');
btnSave.classList.remove('dis');
}else{
ctx.strokeStyle = "rgba(250,250,250,0)";
document.onmousemove = function() {
ctx.globalCompositeOperation = "destination-out";
draw();
ctx.globalCompositeOperation = "source-over"
}
}
canvas.onmouseup = function() {
document.onmousemove = null;
document.onmouseup = null;
};
draw = function(e){
var e = e || window.event,
x2 = e.clientX - canvas.getBoundingClientRect().left,
y2 = e.clientY - canvas.getBoundingClientRect().top,
asin = lineSize * Math.sin(Math.atan((y2 - y1) / (x2 - x1))),
acos = lineSize * Math.cos(Math.atan((y2 - y1) / (x2 - x1))),
x3 = x1 + asin,
y3 = y1 - acos,
x4 = x1 - asin,
y4 = y1 + acos,
x5 = x2 + asin,
y5 = y2 - acos,
x6 = x2 - asin,
y6 = y2 + acos;
ctx.beginPath();
ctx.arc(x2, y2, lineSize, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath();
ctx.beginPath();
ctx.fillStyle = lineColor;
ctx.moveTo(x3, y3);
ctx.lineTo(x5, y5);
ctx.lineTo(x6, y6);
ctx.lineTo(x4, y4);
ctx.fill();
ctx.closePath();
x1 = x2;
y1 = y2;
}
})
// 清除
btnClear.addEventListener('click',function(){
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
this.classList.add('dis');
btnSave.classList.add('dis');
})
//保存图片
btnSave.addEventListener('click',function(){
var imgData = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
var filename = '图片.png';
saveFile(imgData,filename);
this.classList.add('dis');
})
var saveFile = function(data, filename) {
var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
save_link.href = data;
save_link.download = filename;
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
save_link.dispatchEvent(event);
};