您好,登錄后才能下訂單哦!
這篇文章主要介紹了HTML中如何實現一個canvas智繪畫板,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
一、項目介紹
名稱:智繪畫板
技術棧:HTML5,CSS3,JavaScript,移動端
功能描述:
支持PC端和移動端在線繪畫功能
實現任意選擇畫筆顏色、調整畫筆粗細以及橡皮檫擦除等繪畫功能
實現在線畫板的本地保存功能
支持撤銷和返回操作
自定義背景顏色
二、項目效果展示
項目地址 預覽地址
預覽圖
PC端的預覽圖:
移動端的預覽圖:
看完上面的預覽圖和體驗過 智繪畫板 覺得還可以的,記得點個贊哦,不管你是否十分激動,反正我是挺激動的,畢竟自己實現出現的項目效果,挺自豪的,說了一堆廢話,下面就可以動起手來敲代碼,實現自己想要的效果!!!
注:下面實現項目效果主要是關于JavaScript方面的,下面僅僅是提供 實現思路的代碼 , 并非全部代碼 。
三、一步步實現項目效果
(一)分析頁面
通過 用例圖 ,我們知道用戶進入我們這個網站有哪些功能?
用戶可以進行的操作:
畫畫
改變畫筆的粗細
切換畫筆的顏色
使用橡皮檫擦除不想要的部分
清空畫板
將自己畫的東西保存成圖片
進行撤銷和重做操作
切換畫板背景顏色
兼容移動端(支持觸摸)
(二)進行HTML布局
我書寫html的同時,引入了css文件和js文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>智繪畫板</title> <link rel="shortcut icon" href="./image/favicon.png" type="image/x-icon"> <link rel="stylesheet" href="./css/style.css"> </head> <body> <canvas id="canvas"></canvas> <div class="bg-btn"></div> <div class="color-group" id="bgGroup"> <h4>選擇背景顏色:</h4> <ul class="clearfix"> <li class="bgcolor-item" style="background-color: blue;"></li> <li class="bgcolor-item" style="background-color: black;"></li> <li class="bgcolor-item" style="background-color: #FF3333;"></li> <li class="bgcolor-item" style="background-color: #0066FF;"></li> <li class="bgcolor-item" style="background-color: #FFFF33;"></li> <li class="bgcolor-item" style="background-color: #33CC66;"></li> <li class="bgcolor-item" style="background-color: gray;"></li> <li class="bgcolor-item" style="background-color: #F34334;"></li> <li class="bgcolor-item" style="background-color: #fff;box-shadow: 0 1px 2px 0 rgba(32,33,36,0.28);"></li> <li class="bgcolor-item" style="background-color: #9B27AC;"></li> <li class="bgcolor-item" style="background-color: #4CB050;"></li> <li class="bgcolor-item" style="background-color: #029688;"></li> </ul> <i class="closeBtn"></i> </div> <div class="tools"> <div class="container"> <button class="save" id="save" title="保存"></button> <button class="brush active" id="brush" title="畫筆"></button> <button class="eraser" id="eraser" title="橡皮擦"></button> <button class="clear" id="clear" title="清屏"></button> <button class="undo" id="undo" title="撤銷"></button> <button class="redo" id="redo" title="再做"></button> </div> </div> <div class="pen-detail" id="penDetail"> <i class="closeBtn"></i> <p>筆大小</p> <span class="circle-box"><i id="thickness"></i></span> <input type="range" id="range1" min="1" max="10" value="1"> <p>筆顏色</p> <ul class="pen-color clearfix"> <li class="color-item active" style="background-color: black;"></li> <li class="color-item" style="background-color: #FF3333;"></li> <li class="color-item" style="background-color: #99CC00;"></li> <li class="color-item" style="background-color: #0066FF;"></li> <li class="color-item" style="background-color: #FFFF33;"></li> <li class="color-item" style="background-color: #33CC66;"></li> </ul> <p>不透明度</p> <i class="showOpacity"></i> <input type="range" id="range2" min="1" max="10" value="1"> </div> <script src="./js/main.js"></script> </body> </html>
(三)用CSS美化界面
css代碼可以根據個人習慣進行美化界面,所以這里就不寫css的代碼了,大家可以直接看 項目代碼 或者從開發者工具中審查元素觀看。如果有問題可以私聊我,我覺得問題不大。
(四)使用JS實現項目的具體功能
1.準備工作
首先,準備個容器,也就是畫板了,前面的html已經書寫好這個容器,這里純屬是廢話。
<canvas id="canvas"></canvas>
然后初始化js
let canvas = document.getElementById('canvas'); let context = 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。
但是我在電腦上對瀏覽器的窗口進行改變,畫板不會自適應的放縮。解決辦法:
// 記得要執行autoSetSize這個函數哦 function autoSetSize(){ canvasSetSize(); // 當執行這個函數的時候,會先設置canvas的寬高 function canvasSetSize(){ let pageWidth = document.documentElement.clientWidth; let pageHeight = document.documentElement.clientHeight; canvas.width = pageWidth; canvas.height = pageHeight; } // 在窗口大小改變之后,就會觸發resize事件,重新設置canvas的寬高 window.onresize = function(){ canvasSetSize(); } }
2.實現畫畫的功能
實現思路:監聽鼠標事件, 用 drawLine()
方法把記錄的數據畫出來。
初始化當前畫板的畫筆狀態, painting = false
。
當鼠標按下時( mousedown
),把 painting
設為 true
,表示正在畫,鼠標沒松開。把鼠標點記錄下來。
當按下鼠標的時候,鼠標移動( mousemove
)就 把點記錄 下來并畫出來。 如果鼠標移動過快,瀏覽器跟不上繪畫速度,點與點之間會出現間隙,所以我們需要將畫出的點用線連起來( lineTo()
)。
鼠標松開的時候( mouseup
),把 painting
設為 false
。
注: drawCircle
這個方法其實可以不用書寫,這個只是為了讓大家能夠理解開始點擊的位置在哪里?
function listenToUser() { // 定義一個變量初始化畫筆狀態 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); lastPoint = newPoint; } } // 鼠標松開事件 canvas.onmouseup = function(){ painting = false; } } // 畫點函數 function drawCircle(x,y,radius){ // 新建一條路徑,生成之后,圖形繪制命令被指向到路徑上生成路徑。 context.beginPath(); // 畫一個以(x,y)為圓心的以radius為半徑的圓弧(圓), // 從startAngle開始到endAngle結束,按照anticlockwise給定的方向(默認為順時針)來生成。 context.arc(x,y,radius,0,Math.PI*2); // 通過填充路徑的內容區域生成實心的圖形 context.fill(); // 閉合路徑之后圖形繪制命令又重新指向到上下文中。 context.closePath(); } function drawLine(x1,y1,x2,y2){ // 設置線條寬度 context.lineWidth = 10; // 設置線條末端樣式。 context.lineCap = "round"; // 設定線條與線條間接合處的樣式 context.lineJoin = "round"; // moveTo(x,y)將筆觸移動到指定的坐標x以及y上 context.moveTo(x1,y1); // lineTo(x, y) 繪制一條從當前位置到指定x以及y位置的直線 context.lineTo(x2,y2); // 通過線條來繪制圖形輪廓 context.stroke(); context.closePath(); }
3.實現橡皮擦功能
實現思路:
獲取橡皮擦元素
設置橡皮擦初始狀態, eraserEnabled = false
。
監聽橡皮擦 click
事件,點擊橡皮擦,改變橡皮擦狀態, eraserEnabled = true
,并且切換class,實現 被激活 的效果。
eraserEnabled
為 true
時,移動鼠標用 context.clearRect()
實現了 橡皮檫。
但是我發現canvas的API中,可以清除像素的就是clearRect方法,但是clearRect方法的清除區域矩形,畢竟大部分人的習慣中的橡皮擦都是圓形的,所以就引入了剪輯區域這個強大的功能,也就是clip方法。下面的代碼是使用 context.clearRect()
實現了 橡皮檫。請看踩坑部分,了解如何更好的實現橡皮檫。
let eraser = document.getElementById("eraser"); let eraserEnabled = false; // 記得要執行listenToUser這個函數哦 function listenToUser() { // ... 代表省略了之前寫的代碼 // ... // 鼠標按下事件 canvas.onmousedown = function(e){ // ... if(eraserEnabled){//要使用eraser context.clearRect(x-5,y-5,10,10) }else{ lastPoint = {'x':x,'y':y} } } // 鼠標移動事件 canvas.onmousemove = function(e){ let x = e.clientX; let y = e.clientY; if(!painting){return} if(eraserEnabled){ context.clearRect(x-5,y-5,10,10); }else{ var newPoint = {'x':x,'y':y}; drawLine(lastPoint.x, lastPoint.y,newPoint.x, newPoint.y); lastPoint = newPoint; } } // ... } // 點擊橡皮檫 eraser.onclick = function(){ eraserEnabled = true; eraser.classList.add('active'); brush.classList.remove('active'); }
4.實現清屏功能
實現思路:
獲取元素節點。
點擊清空按鈕清空canvas畫布。
let reSetCanvas = document.getElementById("clear"); // 實現清屏 reSetCanvas.onclick = function(){ ctx.clearRect(0,0,canvas.width,canvas.height); setCanvasBg('white'); } // 重新設置canvas背景顏色 function setCanvasBg(color) { ctx.fillStyle = color; ctx.fillRect(0, 0, canvas.width, canvas.height); }
5.實現保存成圖片功能
實現思路:
獲取canvas.toDateURL
在頁面里創建并插入一個a標簽
a標簽href等于canvas.toDateURL,并添加download屬性
點擊保存按鈕,a標簽觸發click事件
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 = 'mypic'+(new Date).getTime(); saveA.target = '_blank'; saveA.click(); }
6.實現改變背景顏色的功能
實現思路:
獲取相應的元素節點。
給每一個class為bgcolor-item的標簽添加點擊事件,當點擊事件觸發時,改變背景顏色。
點擊設置背景顏色的div之外的地方,實現隱藏那個div。
let selectBg = document.querySelector('.bg-btn'); let bgGroup = document.querySelector('.color-group'); let bgcolorBtn = document.querySelectorAll('.bgcolor-item'); let penDetail = document.getElementById("penDetail"); let activeBgColor = '#fff'; // 實現了切換背景顏色 for (let i = 0; i < bgcolorBtn.length; i++) { bgcolorBtn[i].onclick = function (e) { // 阻止冒泡 e.stopPropagation(); for (let i = 0; i < bgcolorBtn.length; i++) { bgcolorBtn[i].classList.remove("active"); this.classList.add("active"); activeBgColor = this.style.backgroundColor; setCanvasBg(activeBgColor); } } } document.onclick = function(){ bgGroup.classList.remove('active'); } selectBg.onclick = function(e){ bgGroup.classList.add('active'); e.stopPropagation(); }
7.實現改變畫筆粗細的功能
實現思路:
實現讓設置畫筆的屬性的對話框出現。
獲取相應的元素節點。
當input=range的元素發生改變的時候,獲取到的值賦值給lWidth。
然后設置context.lineWidth = lWidth。
let range1 = document.getElementById('range1'); let lWidth = 2; let ifPop = false; range1.onchange = function(){ console.log(range1.value); console.log(typeof range1.value) thickness.style.transform = 'scale('+ (parseInt(range1.value)) +')'; console.log(thickness.style.transform ) lWidth = parseInt(range1.value*2); } // 畫線函數 function drawLine(x1,y1,x2,y2){ // ... context.lineWidth = lWidth; // ... } // 點擊畫筆 brush.onclick = function(){ eraserEnabled = false; brush.classList.add('active'); eraser.classList.remove('active'); if(!ifPop){ // 彈出框 console.log('彈一彈') penDetail.classList.add('active'); }else{ penDetail.classList.remove('active'); } ifPop = !ifPop; }
8.實現改變畫筆顏色的功能
實現思路跟 改變畫板背景顏色 的思路類似。
let aColorBtn = document.getElementsByClassName("color-item"); getColor(); function getColor(){ for (let i = 0; i < aColorBtn.length; i++) { aColorBtn[i].onclick = function () { for (let i = 0; i < aColorBtn.length; i++) { aColorBtn[i].classList.remove("active"); this.classList.add("active"); activeColor = this.style.backgroundColor; ctx.fillStyle = activeColor; ctx.strokeStyle = activeColor; } } } }
9.實現改變撤銷和重做的功能
實現思路:
保存快照:每完成一次繪制操作則保存一份 canvas 快照到 canvasHistory
數組(生成快照使用 canvas 的 toDataURL()
方法,生成的是 base64 的圖片);
撤銷和反撤銷:把 canvasHistory
數組中對應索引的快照使用 canvas 的 drawImage()
方法重繪一遍;
繪制新圖像:執行新的繪制操作時,刪除當前位置之后的數組記錄,然后添加新的快照。
let undo = document.getElementById("undo"); let redo = document.getElementById("redo"); // ... canvas.onmouseup = function(){ painting = false; canvasDraw(); } let canvasHistory = []; let step = -1; // 繪制方法 function canvasDraw(){ step++; if(step < canvasHistory.length){ canvasHistory.length = step; // 截斷數組 } // 添加新的繪制到歷史記錄 canvasHistory.push(canvas.toDataURL()); } // 撤銷方法 function canvasUndo(){ if(step > 0){ step--; // ctx.clearRect(0,0,canvas.width,canvas.height); let canvasPic = new Image(); canvasPic.src = canvasHistory[step]; canvasPic.onload = function () { ctx.drawImage(canvasPic, 0, 0); } undo.classList.add('active'); }else{ undo.classList.remove('active'); alert('不能再繼續撤銷了'); } } // 重做方法 function canvasRedo(){ if(step < canvasHistory.length - 1){ step++; let canvasPic = new Image(); canvasPic.src = canvasHistory[step]; canvasPic.onload = function () { // ctx.clearRect(0,0,canvas.width,canvas.height); ctx.drawImage(canvasPic, 0, 0); } redo.classList.add('active'); }else { redo.classList.remove('active') alert('已經是最新的記錄了'); } } undo.onclick = function(){ canvasUndo(); } redo.onclick = function(){ canvasRedo(); }
10.兼容移動端
實現思路:
判斷設備是否支持觸摸
true
,則使用 touch
事件; false
,則使用 mouse
事件
// ... if (document.body.ontouchstart !== undefined) { // 使用touch事件 anvas.ontouchstart = function (e) { // 開始觸摸 } canvas.ontouchmove = function (e) { // 開始滑動 } canvas.ontouchend = function () { // 滑動結束 } }else{ // 使用mouse事件 // ... } // ...
四、踩坑
問題1:在電腦上對瀏覽器的窗口進行改變,畫板不會自適應
解決辦法:
onresize響應事件處理中,獲取到的頁面尺寸參數是變更后的參數 。
當窗口大小發生改變之后,重新設置canvas的寬高,簡單來說,就是窗口改變之后,給canvas.width和canvas.height重新賦值。
// 記得要執行autoSetSize這個函數哦 function autoSetSize(){ canvasSetSize(); // 當執行這個函數的時候,會先設置canvas的寬高 function canvasSetSize(){ let pageWidth = document.documentElement.clientWidth; let pageHeight = document.documentElement.clientHeight; canvas.width = pageWidth; canvas.height = pageHeight; } // 在窗口大小改變之后,就會觸發resize事件,重新設置canvas的寬高 window.onresize = function(){ canvasSetSize(); } }
問題2:當繪制線條寬度比較小的時候還好,一旦比較粗就會出現問題
解決辦法:看一下文檔,得出方法,只需要簡單修改一下 繪制線條的代碼 就行
// 畫線函數 function drawLine(x1,y1,x2,y2){ context.beginPath(); context.lineWidth = lWidth; //-----加入----- // 設置線條末端樣式。 context.lineCap = "round"; // 設定線條與線條間接合處的樣式 context.lineJoin = "round"; //-----加入----- context.moveTo(x1,y1); context.lineTo(x2,y2); context.stroke(); context.closePath(); }
問題3:如何實現圓形的橡皮檫?
解決辦法:
canvas的API中,可以清除像素的就是clearRect方法,但是clearRect方法的清除區域矩形,畢竟大部分人的習慣中的橡皮擦都是圓形的,所以就引入了剪輯區域這個強大的功能,也就是clip方法。用法很簡單:
ctx.save() ctx.beginPath() ctx.arc(x2,y2,a,0,2*Math.PI); ctx.clip() ctx.clearRect(0,0,canvas.width,canvas.height); ctx.restore();
上面那段代碼就實現了圓形區域的擦除,也就是先實現一個圓形路徑,然后把這個路徑作為剪輯區域,再清除像素就行了。有個注意點就是需要先保存繪圖環境,清除完像素后要重置繪圖環境,如果不重置的話以后的繪圖都是會被限制在那個剪輯區域中。
問題4:如何兼容移動端?
1.添加meta標簽
因為瀏覽器初始會將頁面現在手機端顯示時進行縮放,因此我們可以在meta標簽中設置meta viewport屬性,告訴瀏覽器不將頁面進行縮放,頁面寬度=用戶設備屏幕寬度
<meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no, maximum-scale=1.0,minimum-scale=1.0"/> /* 頁面寬度=移動寬度 :width=device-width 用戶不可以縮放:user-scalable=no 縮放比例:initial-scale=1 最大縮放比例:maximum-scale=1.0 最小縮放比例:minimum-scale=1.0 */
2.在移動端幾乎使用的都是touch事件,與PC端不同
由于移動端是觸摸事件,所以要用到H5的屬性touchstart/touchmove/touchend,但是PC端只支持鼠標事件,所以要進行特性檢測。
在 touch
事件里,是通過 .touches[0].clientX
和 .touches[0].clientY
來獲取坐標的,這點要和 mouse
事件區別開。
問題5:出現一個問題就是清空之后,重新畫,然后出現原來的畫的東西
這個嘛,問題不大,只不過是我漏寫context.beginPath(); ,也花了一點時間在上面解決bug,讓我想起“代碼千萬行,注釋第一行;編程不規范,同事兩行淚 ”,還是按照文檔操作規范操作好,真香!!!
1、簡易性:超級文本標記語言版本升級采用超集方式,從而更加靈活方便,適合初學前端開發者使用。 2、可擴展性:超級文本標記語言的廣泛應用帶來了加強功能,增加標識符等要求,超級文本標記語言采取子類元素的方式,為系統擴展帶來保證。 3、平臺無關性:超級文本標記語言能夠在廣泛的平臺上使用,這也是萬維網盛行的一個原因。 4、通用性:HTML是網絡的通用語言,它允許網頁制作人建立文本與圖片相結合的復雜頁面,這些頁面可以被網上任何其他人瀏覽到,無論使用的是什么類型的電腦或瀏覽器。
感謝你能夠認真閱讀完這篇文章,希望小編分享的“HTML中如何實現一個canvas智繪畫板”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。