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); };
這樣看起來雖說好像沒問題,可是這一句有一些問題:html
e.clientX - canvas.offsetLeft,e.clientY - canvas.offsetTop
若是頁面比較高,產生了滾動條,當滾動條向上滑的時候,繪圖的位置就不許確了,示例如:https://codepen.io/jianxiujiu...。canvas
因此此處應該使用:工具
e.clientX - canvas.getBoundingClientRect().left,e.clientY - canvas.getBoundingClientRect().top
來替換。this
簡單的繪製和圖片保存完成了,可是在這種狀況下,線條會有很明顯的鋸齒(靈魂畫手來了)。
spa
有一個簡單粗暴的方法,將線條的邊緣進行模糊:code
ctx.shadowBlur = 1; ctx.shadowColor = iptColor.value;
示例:https://codepen.io/jianxiujiu...
加上邊緣模糊以後,線條明顯柔和了許多,可是仍是有一個問題,就是在畫筆收筆的地方,線條會變小(靈魂畫手又來了)。
htm
在通過搜索查閱以後,發現有一個繪製辦法能夠下降鋸齒的問題。
原理如圖:
繪製一個圓點,在下一個圓點之間,繪製一個矩形進行填充。這樣繪製出來的線條的銜接會是圓滑的。圖片
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...ip
用這個方法,咱們還能夠加上橡皮擦。
橡皮擦的原理是,將橡皮繪製的路徑覆蓋到原來的畫筆上。
這裏咱們將用到canvas的一個屬性globalCompositeOperation,詳解參照:
http://www.w3school.com.cn/ta...rem
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); };