JavaScript使用canvas實現(xiàn)手寫簽名功能
預(yù)覽效果
如果不想閱讀文章,可直接查看源碼
先實現(xiàn)基本需求(能簽名即可)
整理思路
準備一個canvas畫布,得到context對象
指定畫筆的樣式
監(jiān)聽鼠標 / 手指的移動,得到每一次移動在畫布上的坐標點,記錄下來
將這些點繪制到畫布上形成線條
<canvas?width="600"?height="400"?id="canvas"?style="background-color:?#ddd;"></canvas>
為了方便調(diào)試,我們本次僅演示pc端的操作。移動端思路是一樣的,只不過監(jiān)聽的API不同。
常見的操作方式是:當鼠標左鍵按下的時候在畫布上移動鼠標,就可以繪制。沒有按下鼠標時,不管它。
注釋很重要,我盡量寫得很詳細
window.onload?=?function?()?{???? //?默認鼠標是沒有按下的???? let?isDown?=?false;???? //?記錄上一次鼠標的位置???? let?lastX?=?0;?//?x軸???? let?lastY?=?0;?//?y軸???????? //?獲取canvas ??const?canvas?=?document.getElementById("canvas");???? //?獲取canvas的上下文???? const?ctx?=?canvas.getContext("2d");???? //?定義線條的寬度,即畫筆的粗細???? ctx.lineWidth?=?3;???? //?定義畫筆的顏色???? ctx.strokeStyle?=?"#000";???? /** ??????* 定義繪制方法 ????? *?線條其實是由兩個點連起來的一個線段 ????? * 一個又一個的小線段,連起來就組成了一個線條 ??????*?在畫布上繪制線條,主要用到的三個核心方法?????? * moveTo:?是 Canvas 2D API 將一個新的子路徑的起始點移動到?(x,y)?坐標的法。??? * lineTo:?是 Canvas 2D API 使用直線連接子路徑的終點到 x,y 坐標的法。?????? *?當然,定義了起點和終點還不夠,還需要手動調(diào)用開始繪制這個路徑 ? ? ? * startX 和 startY 一起組成了起點的坐標 ????? * endX?和 endY?一起組成了線段終點的坐標 ??????*/ ????function?draw(startX, startY, endX, endY)?{???????? //?起點???????? ctx.moveTo(startX, startY);???????? //?終點???????? ctx.lineTo(endX, endY);???????? // 調(diào)用 stroke,即可看到繪制的線條???????? ctx.stroke();???? }???? //?監(jiān)聽鼠標按下,得到按下時鼠標在畫布上的坐標???? canvas.addEventListener("mousedown",?({?x,?y?})?=>?{ isDown?=?true; // 按下時的點作為起點 lastX?=?x; lastY?=?y; //?創(chuàng)建一個新的路徑 ctx.beginPath(); }, false);???? //?監(jiān)聽鼠標移動???? canvas.addEventListener("mousemove",?({?x,?y?})?=>?{???????????? //?沒有按下就不管 if?(!isDown)?return; //?調(diào)用繪制方法 draw(lastX,?lastY,?x,?y); //?把當前移動時的坐標作為下一次的繪制路徑的起點 lastX?=?x; lastY?=?y; }, false); //?監(jiān)聽鼠標抬起 canvas.addEventListener("mouseup", ()?=>?{ isDown?=?false; //?關(guān)閉路徑 ctx.closePath(); },?false); // 監(jiān)聽鼠標移出 canvas.addEventListener("mouseleave", () => { // 移出canvas范圍,也認為是鼠標抬起了,再移入需要重新按下鼠標,避免移出之后再抬起鼠標,重新進入畫筆還能繼續(xù)畫的問題 isDown = false; ctx.closePath(); }, false); };
以上代碼就實現(xiàn)了最基本的簽名功能。
將canvas導(dǎo)出為圖片
這個功能比較簡單,思路都在注釋里了
//?使用canvas的toDataURL()方法,將畫布內(nèi)容轉(zhuǎn)換為base64格式的圖片數(shù)據(jù): let?imgData?=?canvas.toDataURL('image/png');? //?創(chuàng)建下載鏈接 let?link?=?document.createElement('a'); link.download?=?'picture.png'; link.href?=?imgData; //?觸發(fā)點擊 link.click(); //?移除元素 document.body.removeChild(link);
撤銷和重寫功能
整理思路
要實現(xiàn)撤銷筆畫回到上一步,就要知道上一步畫了什么,就是要記錄下來,我們可以用一個數(shù)組,把每次鼠標移動時得到的坐標放進去。
通過基礎(chǔ)功能我們知道了,畫布上簽名,是由多個線條組成的,而線條是由很多個點組成的。那我們撤銷的時候,是撤銷一條線,即一個筆畫,而不是一個點。
那么,怎么知道哪些點是屬于一個筆畫的呢,就是要給這些點分組,一個筆畫為一組。我們規(guī)定,從鼠標按下到鼠標抬起,這之間移動時產(chǎn)生的所有點為一組,即一個筆畫。用代碼表示,就是有多個數(shù)組,所以我們定一個二維數(shù)組來保存所有的點。
改寫一下前面的代碼
window.onload?=?function?()?{???? //?默認鼠標是沒有按下的???? let?isDown?=?false;???? //?//?記錄上一次鼠標的位置???? //?let?lastX?=?0;?//?x軸???? //?let?lastY?=?0;?//?y軸?????? //?這次要用數(shù)組來記錄???? let?points?=?[];?//?這是一個筆畫的點???? let?allPonits?=?[];?//?這是所有筆畫的點???????? //?獲取canvas元素????? const?canvas?=?document.getElementById("canvas");????//?獲取canvas的上下文???? const?ctx?=?canvas.getContext("2d");????//?定義線條的寬度,即畫筆的粗細???? ctx.lineWidth?=?3;????//?定義畫筆的顏色???? ctx.strokeStyle?=?"#000";???????? function?draw(startX, startY, endX, endY)?{???????? //?起點???????? ctx.moveTo(startX, startY);???????? //?終點???????? ctx.lineTo(endX, endY);???????? // 調(diào)用 stroke,即可看到繪制的線條???????? ctx.stroke();???? }???? //?監(jiān)聽鼠標按下,得到按下時鼠標在畫布上的坐標???? canvas.addEventListener("mousedown", ({?x,?y?})?=>?{???????????? isDown?=?true;???????????? //?lastX?=?x; //?lastY?=?y; // 保存當前坐標作為起點 points.push({?x,?y?});???????????? //?創(chuàng)建一個新的路徑???????????? ctx.beginPath();???????? }, false);???? //?監(jiān)聽鼠標移動???? canvas.addEventListener("mousemove", ({?x,?y?})?=>?{ //?沒有按下就不管 if?(!isDown)?return; //?調(diào)用繪制方法 //?draw(lastX,?lastY,?x,?y); //?把當前移動時的坐標作為下一次的繪制路徑的起點???????????? //?lastX?=?x; //?lastY?=?y; // 每次都取最后一個點,作為繪制的起點 const?lastPoint?=?points.at(-1); draw(lastPoint.x,?lastPoint.y,?x,?y); //?把當前的點保存起來,又作為下一次繪制的起點 points.push({?x,?y?}); }, false); //?監(jiān)聽鼠標抬起???? canvas.addEventListener("mouseup", (e)?=>?{???????????? isDown?=?false; //?關(guān)閉路徑 ctx.closePath(); //?鼠標抬起,說明當前這一筆就結(jié)束了,把這一筆的所有點的數(shù)組放到總的里面 allPonits.push(points); //?清空這一筆畫,為下一筆畫做準備???????????? points?=?[]; }, false); // 監(jiān)聽鼠標移出 canvas.addEventListener("mouseleave", () => { // 移出canvas范圍,也認為是鼠標抬起了,再移入需要重新按下鼠標,避免移出之后再抬起鼠標,重新進入畫筆還能繼續(xù)畫的問題 isDown = false; // 關(guān)閉畫筆 ctx.closePath(); // 如果是先抬起鼠標再移出,那么points里面為空,就不保存了 // 如果是先移出范圍,移出時就保存,這樣也不會觸發(fā)上面的監(jiān)聽鼠標抬起事件,也不會push。 if (points.length) { allPonits.push(points); } // 移出時也清空,因為無法判斷是先抬起還是先移出的。 points = []; }, false); };
在頁面上加兩個按鈕
<div> ??<button?id="prev">上一步</button> ??<button?id="reset">重寫</button> </div>
const?prev?=?document.getElementById("prev"); const?reset?=?document.getElementById("reset"); //?清空畫布 function?resetPath()?{ ctx.clearRect(0,?0,?canvas.width,?canvas.height); } //?上一步 prev.addEventListener("click",?(e)?=>?{ //?canvas本身不會記錄用戶的每一步操作 //?要回到上一步,只能一次性清空所有的 resetPath(); //?刪除最后一個筆畫 allPonits.pop(); //?遍歷所有的筆畫并重新繪制 // allPoints 是個二維數(shù)組 allPonits.forEach((ps)?=>?{ ps.forEach((item,?index)?=>?{ // 下一個坐標點 let?next?=?ps[index?+?1]; if?(next)?{ // 有下一個點才執(zhí)行,否則到最后一個會報錯 // 開始重新繪制 ctx.beginPath(); draw(item.x,?item.y,?next.x,?next.y); ctx.closePath(); } });?? }); }); //?重寫 reset.addEventListener("click", ()?=>?{ //?點擊重寫時清空畫布,并清空所有的點???? resetPath(); allPonits?=?[]; }, false);
到這里,我們就完成了canvas手寫簽名,并且實現(xiàn)了撤銷和重寫,以及導(dǎo)出為圖片的功能。
以上就是JavaScript使用canvas實現(xiàn)手寫簽名功能的詳細內(nèi)容,更多關(guān)于JavaScript canvas手寫簽名的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JS 設(shè)計模式之:單例模式定義與實現(xiàn)方法淺析
這篇文章主要介紹了JS 設(shè)計模式之:單例模式,結(jié)合實例形式分析了JS 單例模式原理、定義、實現(xiàn)方法與相關(guān)注意事項,需要的朋友可以參考下2020-05-05淺談 Webpack 如何處理圖片(開發(fā)、打包、優(yōu)化)
這篇文章主要介紹了淺談 Webpack 如何處理圖片(開發(fā)、打包、優(yōu)化),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-05-05