本文同步於我的博客:https://zhoushuo.me/blog/2018/03/11/drawing-borad/git
前些天學習了HTML5
的<canvas>
元素,今天就來實踐一下,用canvas
作一個畫板。github
首先說一下要實現的功能:canvas
好了,廢話少說,先看最終效果:https://zhoushuozh.github.io/drawingborad數組
首先,準備個容器,也就是畫板了。瀏覽器
<canvas id="drawing-board"></canvas>
而後初始化js:app
let canvas = document.getElementById("drawing-board"); let ctx = canvas.getContext("2d");
我想把畫板作成全屏的,因此接下來設置一下canvas
的寬高。函數
let pageWidth = document.documentElement.clientWidth; let pageHeight = document.documentElement.clientHeight; canvas.width = pageWidth; canvas.height = pageHeight;
因爲部分IE不支持canvas
,若是要兼容IE,咱們能夠建立一個canvas
,而後使用excanvas
初始化,針對IE加上exCanvas.js,這裏咱們暫時先不考慮IE。性能
實現思路:監聽鼠標事件,用drawCircle()
方法把記錄的數據畫出來。學習
painting = false
,mousedown
),把painting
設爲true
,表示正在畫,鼠標沒鬆開。把鼠標點記錄下來。mousemove
)就把點記錄下來並畫出來。lineTo()
)。mouseup
),把painting
設爲false
。代碼:優化
let painting = false; let lastPoint = {x: undefined, y: undefined}; //鼠標按下事件 canvas.onmousedown = function (e) { painting = true; let x = e.clientX; let y = e.clientY; lastPoint = {"x": x, "y": y}; drawCircle(x, y, 5); }; //鼠標移動事件 canvas.onmousemove = function (e) { if (painting) { let x = e.clientX; let y = e.clientY; let newPoint = {"x": x, "y": y}; drawLine(lastPoint.x, lastPoint.y, newPoint.x, newPoint.y,clear); lastPoint = newPoint; } }; //鼠標鬆開事件 canvas.onmouseup = function () { painting = false; } // 畫點函數 function drawCircle(x, y, radius) { ctx.save(); ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI * 2); ctx.fill(); } // 劃線函數 function drawLine(x1, y1, x2, y2) { ctx.lineWidth = 3; ctx.lineCap = "round"; ctx.lineJoin = "round"; ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); ctx.stroke(); ctx.closePath(); }
實現思路
clear = false
。click
事件,點擊橡皮擦,改變橡皮擦狀態,clear = true
。clear
爲true
時,移動鼠標使用canvas
剪裁(clip()
)擦除畫布。let eraser = document.getElementById("eraser"); let clear = false; eraser.onclick = function () { clear = true; }; ... if (clear) { ctx.save(); ctx.globalCompositeOperation = "destination-out"; ctx.stroke(); ctx.closePath(); ctx.clip(); ctx.clearRect(0,0,canvas.width,canvas.height); ctx.restore(); } ...
注意,在canvas
中的裁剪和平時的裁剪功能不同在這裏,裁剪是指在裁剪區域去顯示咱們所畫的圖
實現思路:
true
,則使用touch
事件;false
,則使用mouse
事件代碼:
... if (document.body.ontouchstart !== undefined) { // 使用touch事件 anvas.ontouchstart = function (e) { // 開始觸摸 } canvas.ontouchmove = function (e) { // 開始滑動 } canvas.ontouchend = function () { // 滑動結束 } }else{ // 使用mouse事件 ... } ...
這裏須要注意的一點是,在touch
事件裏,是經過.touches[0].clientX
和.touches[0].clientY
來獲取座標的,這點要和mouse
事件區別開。
實現思路:
ctx.strokeStyle
。代碼:
let aColorBtn = document.getElementsByClassName("color-item"); for (let i = 0; i < aColorBtn.length; i++) { aColorBtn[i].onclick = function () { for (let i = 0; i < aColorBtn.length; i++) { activeColor = this.style.backgroundColor; ctx.strokeStyle = activeColor; } }
實現思路:
代碼:
let reSetCanvas = document.getElementById("clear"); reSetCanvas.onclick = function () { ctx.clearRect(0, 0, canvas.width, canvas.height); };
實現思路:
ctx.lineWidth
代碼:
let range = document.getElementById("range"); range.onchange = function(){ lWidth = this.value; };
實現思路:
代碼:
let save = document.getElementById("save"); save.onclick = function () { let imgUrl = canvas.toDataURL("image/png"); let saveA = document.createElement("a"); document.body.appendChild(saveA); saveA.href = imgUrl; saveA.download = "zspic" + (new Date).getTime(); saveA.target = "_blank"; saveA.click(); };
實現思路:
代碼:
canvas.ontouchstart = function (e) { // 在這裏儲存繪圖表面 this.firstDot = ctx.getImageData(0, 0, canvas.width, canvas.height); saveData(this.firstDot); ... } let undo = document.getElementById("undo"); let historyDeta = []; function saveData (data) { (historyDeta.length === 10) && (historyDeta.shift()); // 上限爲儲存10步,太多了怕掛掉 historyDeta.push(data); } undo.onclick = function(){ if(historyDeta.length < 1) return false; ctx.putImageData(historyDeta[historyDeta.length - 1], 0, 0); historyDeta.pop() };
由於每次儲存都是將一張圖片存入內存,對性能影響較大,因此在這裏設置了儲存上限。
這裏用的知識點主要有:監聽mouse
、touch
事件,以及canvas
的moveTo()
、lineTo()
、stroke()
、clip()
、clearRect()
等方法。其實還有不少效果能夠實現,好比說噴霧效果,蠟筆效果,藝術畫效果等等。往後有時間我會繼續對這個畫板進行優化,增長一些新的功能,同時歡迎你們留言提問或者提出批評建議。