JavaScript實(shí)現(xiàn)簽名板功能
簽名板在網(wǎng)頁(yè)應(yīng)用中扮演著重要角色,它們能夠記錄用戶的手寫(xiě)簽名或者繪圖,從而提升了用戶體驗(yàn)。
在接下來(lái)的內(nèi)容中,我將指導(dǎo)你如何利用 JavaScript 來(lái)開(kāi)發(fā)一個(gè)功能豐富的簽名板。這個(gè)簽名板不僅支持自定義和響應(yīng)式設(shè)計(jì),還具備對(duì)觸摸設(shè)備的兼容、多樣的筆觸樣式選擇,以及將簽名導(dǎo)出為不同格式圖片的能力。此外,我們還將探索如何整合像 signature_pad 這樣的先進(jìn)工具來(lái)增強(qiáng)簽名板的功能。
開(kāi)始
讓我們使用純 HTML、CSS 和 JavaScript 創(chuàng)建一個(gè)簡(jiǎn)單的簽名板。
首先,HTML 文件——在你的工作目錄中創(chuàng)建一個(gè) index.html
文件:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>JavaScript簽名板</title> <link rel="stylesheet" href="styles.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" > </head> <body> <div class="signature-container"> <canvas id="signature-pad" width="400" height="200"></canvas> <button id="clear">清除</button> </div> <script src="script.js"></script> </body> </html>
我們將采用 <canvas>
標(biāo)簽來(lái)構(gòu)建簽名板。選擇畫(huà)布作為工具是因?yàn)樗鼮槲覀兲峁┝艘韵鹿δ埽?/p>
- 利用 JavaScript 實(shí)現(xiàn)自由手寫(xiě),這對(duì)于獲取簽名是必不可少的。
- 對(duì)畫(huà)布的外觀和行為進(jìn)行個(gè)性化定制,包括線條的顏色、粗細(xì)以及風(fēng)格。
- 兼容多樣的鼠標(biāo)和觸摸事件,捕捉用戶在繪圖、移動(dòng)以及提筆或松手時(shí)的互動(dòng)。
- 利用 toDataURL 方法將簽名以圖像形式導(dǎo)出(如 PNG 或 JPEG),這在保存簽名或?qū)⑵渖蟼髦练?wù)器時(shí)非常有用。
接下來(lái),我們將通過(guò) styles.css
為這個(gè)頁(yè)面添加一些樣式,以提升其視覺(jué)效果。
body { display: flex; justify - content: center; align - items: center; height: 100vh; background - color: #f0f0f0; margin: 0; } .signature - container { display: flex; flex - direction: column; align - items: center; } canvas { border: 1px solid#000; background - color: #fff; } button { margin - top: 10px; padding: 5px 10px; cursor: pointer; }
然后向目錄中添加一個(gè) script.js
文件:
document.addEventListener('DOMContentLoaded', function() { var canvas = document.getElementById('signature-pad'); var ctx = canvas.getContext('2d'); var drawing = false; canvas.addEventListener('mousedown', function(e) { drawing = true; ctx.beginPath(); ctx.moveTo(e.offsetX, e.offsetY); }); canvas.addEventListener('mousemove', function(e) { if (drawing) { ctx.lineTo(e.offsetX, e.offsetY); ctx.stroke(); } }); canvas.addEventListener('mouseup', function() { drawing = false; }); canvas.addEventListener('mouseout', function() { drawing = false; }); document.getElementById('clear').addEventListener('click', function() { ctx.clearRect(0, 0, canvas.width, canvas.height); }); });
讓我們?cè)敿?xì)解釋一下這段代碼的工作原理:
- 我們首先通過(guò)
canvas
變量來(lái)綁定到頁(yè)面上的<canvas>
元素。 - 接著,
ctx
變量用于獲取該畫(huà)布的 2D 渲染上下文,它包含了繪制圖形所需的各種方法和屬性。 - 當(dāng)用戶按下鼠標(biāo)按鈕時(shí),我們通過(guò)一個(gè)
mousedown
事件監(jiān)聽(tīng)器來(lái)觸發(fā)繪圖動(dòng)作。此時(shí),ctx.beginPath()
用于開(kāi)啟新的繪圖路徑,而ctx.moveTo(e.offsetX, e.offsetY)
則將畫(huà)筆定位到鼠標(biāo)點(diǎn)擊的準(zhǔn)確位置。 - 用戶移動(dòng)鼠標(biāo)時(shí),
mousemove
事件監(jiān)聽(tīng)器會(huì)根據(jù)鼠標(biāo)的當(dāng)前位置繪制線條。ctx.lineTo(e.offsetX, e.offsetY)
命令畫(huà)筆繪制一條直線到新位置,隨后ctx.stroke()
將這條線實(shí)際畫(huà)到畫(huà)布上。 - 為了結(jié)束繪圖,我們?cè)O(shè)置了
mouseup
和mouseout
兩個(gè)事件監(jiān)聽(tīng)器,分別在鼠標(biāo)按鈕釋放和鼠標(biāo)光標(biāo)移出畫(huà)布時(shí)停止繪圖。此外,當(dāng)用戶點(diǎn)擊清除按鈕時(shí),ctx.clearRect(0, 0, canvas.width, canvas.height);
用于清除整個(gè)畫(huà)布,為新的繪圖做準(zhǔn)備。
添加觸摸支持
這個(gè)示例主要是為鼠標(biāo)事件設(shè)置的,但它也可以很容易地?cái)U(kuò)展到支持觸摸設(shè)備。以下是 JavaScript 處理觸摸事件:
document.addEventListener('DOMContentLoaded', function() { var canvas = document.getElementById('signature-pad'); var ctx = canvas.getContext('2d'); var drawing = false; function startDrawing(e) { drawing = true; ctx.beginPath(); ctx.moveTo(e.offsetX || e.touches[0].clientX - canvas.offsetLeft, e.offsetY || e.touches[0].clientY - canvas.offsetTop); } function draw(e) { if (drawing) { ctx.lineTo(e.offsetX || e.touches[0].clientX - canvas.offsetLeft, e.offsetY || e.touches[0].clientY - canvas.offsetTop); ctx.stroke(); } } function stopDrawing() { drawing = false; } // 鼠標(biāo)事件 canvas.addEventListener('mousedown', startDrawing); canvas.addEventListener('mousemove', draw); canvas.addEventListener('mouseup', stopDrawing); canvas.addEventListener('mouseout', stopDrawing); // 觸摸事件 canvas.addEventListener('touchstart', startDrawing); canvas.addEventListener('touchmove', draw); canvas.addEventListener('touchend', stopDrawing); canvas.addEventListener('touchcancel', stopDrawing); document.getElementById('clear').addEventListener('click', function() { ctx.clearRect(0, 0, canvas.width, canvas.height); }); });
對(duì)于觸摸事件,使用 e.touches[0].clientX
和 e.touches[0].clientY
來(lái)獲取觸摸坐標(biāo)。為了考慮畫(huà)布的位置,使用 canvas.offsetLeft
和 canvas.offsetTop
進(jìn)行調(diào)整:
自定義
讓我們給簽名板增添一些新功能,比如讓用戶能夠挑選不同的筆觸效果。為此,我們將執(zhí)行以下關(guān)鍵步驟:
- 在 HTML 代碼中嵌入一個(gè)
<select>
標(biāo)簽,它將包含兩個(gè)選項(xiàng):Pen
和Brush
,從而讓用戶可以挑選他們喜歡的筆觸風(fēng)格。 - 為這些新增的控件編寫(xiě)相應(yīng)的 CSS 樣式,確保它們?cè)陧?yè)面上正確地顯示。
- 為下拉選擇菜單設(shè)置一個(gè)事件監(jiān)聽(tīng)器,以便在用戶選擇不同的選項(xiàng)時(shí),動(dòng)態(tài)地改變筆觸樣式。
- 根據(jù)用戶所選擇的筆觸樣式,調(diào)整
ctx.lineWidth
(線寬)和ctx.lineCap
(線帽形狀)的值,以反映不同的視覺(jué)效果。
以下是需要添加到 HTML 文件中的代碼片段:
<div class="controls"> <select id="stroke-style"> <option value="pen"> 鋼筆 </option> <option value="brush"> 刷子 </option> </select> <button id="clear"> JavaScript簽名板 </button> </div>
然后我們更新我們的 CSS:
body { display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f0f0f0; margin: 0; } .signature-container { display: flex; flex-direction: column; align-items: center; } canvas { border: 1px solid #000; background-color: #fff; } .controls { margin-top: 10px; display: flex; gap: 10px; } button, select { padding: 5px 10px; cursor: pointer; }
最后,更新 script.js
:
document.addEventListener('DOMContentLoaded', function() { var canvas = document.getElementById('signature-pad'); var ctx = canvas.getContext('2d'); var drawing = false; var strokeStyle = 'pen'; function startDrawing(e) { drawing = true; ctx.beginPath(); ctx.moveTo(e.offsetX || e.touches[0].clientX - canvas.offsetLeft, e.offsetY || e.touches[0].clientY - canvas.offsetTop); } function draw(e) { if (drawing) { ctx.lineTo(e.offsetX || e.touches[0].clientX - canvas.offsetLeft, e.offsetY || e.touches[0].clientY - canvas.offsetTop); ctx.stroke(); } } function stopDrawing() { drawing = false; } // 鼠標(biāo)事件 canvas.addEventListener('mousedown', startDrawing); canvas.addEventListener('mousemove', draw); canvas.addEventListener('mouseup', stopDrawing); canvas.addEventListener('mouseout', stopDrawing); // 觸摸事件 canvas.addEventListener('touchstart', startDrawing); canvas.addEventListener('touchmove', draw); canvas.addEventListener('touchend', stopDrawing); canvas.addEventListener('touchcancel', stopDrawing); document.getElementById('clear').addEventListener('click', function() { ctx.clearRect(0, 0, canvas.width, canvas.height); }); document.getElementById('stroke-style').addEventListener('change', function(e) { strokeStyle = e.target.value; if (strokeStyle === 'pen') { ctx.lineWidth = 2; ctx.lineCap = 'round'; } else if (strokeStyle === 'brush') { ctx.lineWidth = 5; ctx.lineCap = 'round'; } }); // 設(shè)置初始筆畫(huà)樣式 ctx.lineWidth = 2; ctx.lineCap = 'round'; });
處理響應(yīng)性
當(dāng)屏幕縮小時(shí),簽名板可能因?yàn)槿鄙夙憫?yīng)式設(shè)計(jì)而無(wú)法正常工作。為了改善這一點(diǎn),我們可以增強(qiáng)應(yīng)用的響應(yīng)性,使其在小屏幕設(shè)備上也能輕松進(jìn)行簽名操作。
關(guān)鍵在于確保畫(huà)布及其容器能夠靈活適應(yīng)不同的屏幕尺寸。這需要我們根據(jù)瀏覽器窗口的大小來(lái)動(dòng)態(tài)調(diào)整畫(huà)布的尺寸。
具體來(lái)說(shuō),我們需要做出以下調(diào)整:
- 為
.signature-container
設(shè)置一個(gè)靈活的寬度,比如占滿90%的可用空間,并設(shè)定一個(gè)最大寬度限制,比如600像素。 - 將畫(huà)布元素的寬度設(shè)置為
width: 100%
,高度設(shè)置為height: auto
,以實(shí)現(xiàn)響應(yīng)式效果。
在 JavaScript 代碼中,我們需要執(zhí)行以下操作:
- 創(chuàng)建一個(gè)名為
resizeCanvas
的函數(shù),用以根據(jù)其容器尺寸的變化來(lái)調(diào)整畫(huà)布的大小。 - 在頁(yè)面加載時(shí)首先調(diào)用
resizeCanvas
函數(shù),并在窗口大小發(fā)生變化時(shí)添加一個(gè)事件監(jiān)聽(tīng)器,確保畫(huà)布能夠隨著窗口大小的變化而動(dòng)態(tài)調(diào)整尺寸。
按照上述說(shuō)明,我們需要更新 styles.css
文件,以實(shí)現(xiàn)所需的樣式調(diào)整。
body { display: flex; justify - content: center; align - items: center; height: 100vh; background - color: #f0f0f0; margin: 0; } .signature - container { display: flex; flex - direction: column; align - items: center; width: 90 % ; max - width: 600px; } canvas { border: 1px solid#000; background - color: #fff; width: 100 % ; height: auto; } .controls { margin - top: 10px; display: flex; gap: 10px; } button, select { padding: 5px 10px; cursor: pointer; }
這是在添加之后 script.js
的樣子:
document.addEventListener('DOMContentLoaded', function() { var canvas = document.getElementById('signature-pad'); var ctx = canvas.getContext('2d'); var drawing = false; var strokeStyle = 'pen'; function resizeCanvas() { canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; ctx.lineWidth = strokeStyle === 'pen' ? 2 : 5; ctx.lineCap = 'round'; } function startDrawing(e) { drawing = true; ctx.beginPath(); ctx.moveTo(e.offsetX || e.touches[0].clientX - canvas.offsetLeft, e.offsetY || e.touches[0].clientY - canvas.offsetTop); } function draw(e) { if (drawing) { ctx.lineTo(e.offsetX || e.touches[0].clientX - canvas.offsetLeft, e.offsetY || e.touches[0].clientY - canvas.offsetTop); ctx.stroke(); } } function stopDrawing() { drawing = false; } // 鼠標(biāo)事件 canvas.addEventListener('mousedown', startDrawing); canvas.addEventListener('mousemove', draw); canvas.addEventListener('mouseup', stopDrawing); canvas.addEventListener('mouseout', stopDrawing); // 觸摸事件 canvas.addEventListener('touchstart', startDrawing); canvas.addEventListener('touchmove', draw); canvas.addEventListener('touchend', stopDrawing); canvas.addEventListener('touchcancel', stopDrawing); document.getElementById('clear').addEventListener('click', function() { ctx.clearRect(0, 0, canvas.width, canvas.height); }); document.getElementById('stroke-style').addEventListener('change', function(e) { strokeStyle = e.target.value; ctx.lineWidth = strokeStyle === 'pen' ? 2 : 5; }); // 初始畫(huà)布設(shè)置 resizeCanvas(); window.addEventListener('resize', resizeCanvas); });
你可能會(huì)觀察到,當(dāng)改變?yōu)g覽器窗口大小時(shí),畫(huà)布上的簽名會(huì)消失,這是因?yàn)檎{(diào)整畫(huà)布尺寸會(huì)自動(dòng)清除其內(nèi)容。這是 <canvas>
元素的標(biāo)準(zhǔn)行為。不過(guò),我們可以通過(guò)一些技巧來(lái)避免這個(gè)問(wèn)題。
為了在縮放畫(huà)布時(shí)保持簽名的完整性,并且能夠正確地對(duì)其進(jìn)行縮放,我們需要先保存畫(huà)布上的簽名,然后在調(diào)整畫(huà)布尺寸后再重新繪制它。我們可以通過(guò) toDataURL
方法來(lái)保存畫(huà)布上的簽名。
HTMLCanvasElement.toDataURL()
方法能夠根據(jù)你指定的圖片格式,將畫(huà)布上的內(nèi)容轉(zhuǎn)換成一個(gè)數(shù)據(jù) URL。
現(xiàn)在,讓我們來(lái)看一下如何保存簽名,以便在窗口大小調(diào)整后可以將其恢復(fù)。我們需要做的調(diào)整如下:
- 定義一個(gè)
signatureData
變量來(lái)存儲(chǔ)簽名的當(dāng)前狀態(tài),以數(shù)據(jù) URL 的形式。 - 修改
resizeCanvas
函數(shù),使其能在調(diào)整畫(huà)布大小時(shí),先保存簽名,再調(diào)整尺寸,并最終重新繪制簽名。 - 創(chuàng)建一個(gè)
Image
對(duì)象來(lái)加載保存的簽名,并在調(diào)整尺寸后的畫(huà)布上將其恢復(fù)。 - 當(dāng)完成繪畫(huà)時(shí),使用
canvas.toDataURL()
方法更新signatureData
變量,以便存儲(chǔ)當(dāng)前的畫(huà)布內(nèi)容。 - 當(dāng)用戶點(diǎn)擊清除按鈕時(shí),將
signatureData
變量重置為null
,以清除保存的簽名數(shù)據(jù)。
實(shí)施了上述更改后,我們的 JavaScript 文件將如下所示:
document.addEventListener('DOMContentLoaded', function() { var canvas = document.getElementById('signature-pad'); var ctx = canvas.getContext('2d'); var drawing = false; var strokeStyle = 'pen'; var signatureData = null; function resizeCanvas() { if (signatureData) { var img = new Image(); img.src = signatureData; img.onload = function() { ctx.clearRect(0, 0, canvas.width, canvas.height); canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; ctx.drawImage(img, 0, 0, canvas.width, canvas.height); setStrokeStyle(); }; } else { canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; setStrokeStyle(); } } function setStrokeStyle() { if (strokeStyle === 'pen') { ctx.lineWidth = 2; ctx.lineCap = 'round'; } else if (strokeStyle === 'brush') { ctx.lineWidth = 5; ctx.lineCap = 'round'; } } function startDrawing(e) { drawing = true; ctx.beginPath(); ctx.moveTo(e.offsetX || e.touches[0].clientX - canvas.offsetLeft, e.offsetY || e.touches[0].clientY - canvas.offsetTop); } function draw(e) { if (drawing) { ctx.lineTo(e.offsetX || e.touches[0].clientX - canvas.offsetLeft, e.offsetY || e.touches[0].clientY - canvas.offsetTop); ctx.stroke(); } } function stopDrawing() { drawing = false; signatureData = canvas.toDataURL(); } // 鼠標(biāo)事件 canvas.addEventListener('mousedown', startDrawing); canvas.addEventListener('mousemove', draw); canvas.addEventListener('mouseup', stopDrawing); canvas.addEventListener('mouseout', stopDrawing); // 觸摸事件 canvas.addEventListener('touchstart', startDrawing); canvas.addEventListener('touchmove', draw); canvas.addEventListener('touchend', stopDrawing); canvas.addEventListener('touchcancel', stopDrawing); document.getElementById('clear').addEventListener('click', function() { ctx.clearRect(0, 0, canvas.width, canvas.height); signatureData = null; }); document.getElementById('stroke-style').addEventListener('change', function(e) { strokeStyle = e.target.value; setStrokeStyle(); }); // 初始畫(huà)布設(shè)置 resizeCanvas(); window.addEventListener('resize', resizeCanvas); });
保存和導(dǎo)出
我們將進(jìn)一步增強(qiáng)功能,允許用戶將他們的簽名以 PNG 或 JPEG 格式導(dǎo)出,且這些圖片將擁有白色背景。為此,我們將執(zhí)行以下步驟:
- 在 HTML 代碼中增加兩個(gè)按鈕,分別用于將簽名導(dǎo)出為 PNG 和 JPEG 文件。
- 開(kāi)發(fā)一個(gè)名為
exportCanvas
的函數(shù),該函數(shù)負(fù)責(zé)將畫(huà)布內(nèi)容(包括白色背景)導(dǎo)出。這一過(guò)程涉及到創(chuàng)建一個(gè)新畫(huà)布,將白色填充為背景色,然后將當(dāng)前簽名繪制上去,最后將其保存為 PNG 或 JPEG 文件。
以下是經(jīng)過(guò)這些更改后的 HTML 代碼示例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title> JavaScript簽名板 </title> <link rel="stylesheet" href="styles.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" > </head> <body> <div class="signature-container"> <canvas id="signature-pad" width="400" height="200"> </canvas> <div class="controls"> <button id="clear"> 清除 </button> <button id="export-png"> 導(dǎo)出png格式 </button> <button id="export-jpeg"> 導(dǎo)出jpeg格式 </button> </div> </div> <script src="script.js"> </script> </body> </html>
這是在添加之后 JavaScript 文件的樣子:
document.addEventListener('DOMContentLoaded', function() { var canvas = document.getElementById('signature-pad'); var ctx = canvas.getContext('2d'); var drawing = false; var strokeStyle = 'pen'; var signatureData = null; function resizeCanvas() { if (signatureData) { var img = new Image(); img.src = signatureData; img.onload = function() { ctx.clearRect(0, 0, canvas.width, canvas.height); canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; ctx.drawImage(img, 0, 0, canvas.width, canvas.height); setStrokeStyle(); }; } else { canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; setStrokeStyle(); } } function setStrokeStyle() { if (strokeStyle === 'pen') { ctx.lineWidth = 2; ctx.lineCap = 'round'; } else if (strokeStyle === 'brush') { ctx.lineWidth = 5; ctx.lineCap = 'round'; } } function startDrawing(e) { drawing = true; ctx.beginPath(); ctx.moveTo(e.offsetX || e.touches[0].clientX - canvas.offsetLeft, e.offsetY || e.touches[0].clientY - canvas.offsetTop); } function draw(e) { if (drawing) { ctx.lineTo(e.offsetX || e.touches[0].clientX - canvas.offsetLeft, e.offsetY || e.touches[0].clientY - canvas.offsetTop); ctx.stroke(); } } function stopDrawing() { drawing = false; signatureData = canvas.toDataURL(); } function exportCanvas(format) { var exportCanvas = document.createElement('canvas'); exportCanvas.width = canvas.width; exportCanvas.height = canvas.height; var exportCtx = exportCanvas.getContext('2d'); // 用白色填充背景 exportCtx.fillStyle = '#fff'; exportCtx.fillRect(0, 0, exportCanvas.width, exportCanvas.height); // 繪制簽名 exportCtx.drawImage(canvas, 0, 0); // 導(dǎo)出畫(huà)布 var dataURL = exportCanvas.toDataURL(`image / $ { format }`); var link = document.createElement('a'); link.href = dataURL; link.download = `signature.$ { format }`; link.click(); } // 鼠標(biāo)事件 canvas.addEventListener('mousedown', startDrawing); canvas.addEventListener('mousemove', draw); canvas.addEventListener('mouseup', stopDrawing); canvas.addEventListener('mouseout', stopDrawing); // 觸摸事件 canvas.addEventListener('touchstart', startDrawing); canvas.addEventListener('touchmove', draw); canvas.addEventListener('touchend', stopDrawing); canvas.addEventListener('touchcancel', stopDrawing); document.getElementById('clear').addEventListener('click', function() { ctx.clearRect(0, 0, canvas.width, canvas.height); signatureData = null; }); document.getElementById('stroke-style').addEventListener('change', function(e) { strokeStyle = e.target.value; setStrokeStyle(); }); document.getElementById('export-png').addEventListener('click', function() { exportCanvas('png'); }); document.getElementById('export-jpeg').addEventListener('click', function() { exportCanvas('jpeg'); }); // 初始畫(huà)布設(shè)置 resizeCanvas(); window.addEventListener('resize', resizeCanvas); });
添加更多功能
通過(guò)引入外部庫(kù),我們可以簡(jiǎn)化簽名板中一些復(fù)雜功能的實(shí)現(xiàn)過(guò)程。我們將采用 signature_pad
這個(gè)庫(kù),它非常出色,能夠幫助我們輕松地實(shí)現(xiàn)之前討論過(guò)的眾多功能,并且它還能讓我們生成更加流暢的簽名效果。
首先,我們需要在 HTML 文檔中引入 signature_pad
庫(kù):
<script src="https://cdn.jsdelivr.net/npm/signature_pad@4.1.7/dist/signature_pad.umd.min.js"></script>
然后添加必要的按鈕。這是最終 HTML 的樣子:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title> JavaScript簽名板 </title> <link rel="stylesheet" href="styles.css" rel="external nofollow" rel="external nofollow" rel="external nofollow" > <script src="https://cdn.jsdelivr.net/npm/signature_pad@4.1.7/dist/signature_pad.umd.min.js"> </script> </head> <body> <div class="signature-container"> <canvas id="signature-pad" width="600" height="400"> </canvas> <div class="controls"> <button id="undo"> 撤銷 </button> <button id="redo"> 重做 </button> <button id="clear"> 清除 </button> <button id="save-png"> 導(dǎo)出png格式 </button> <button id="save-jpeg"> 導(dǎo)出jpeg格式 </button> </div> </div> <script src="script.js"> </script> </body> </html>
更新樣式:
body { display: flex; justify - content: center; align - items: center; height: 100vh; background - color: #f0f0f0; margin: 0; } .signature - container { display: flex; flex - direction: column; align - items: center; width: 90 % ; max - width: 600px; } canvas { border: 1px solid#000; background - color: #fff; width: 100 % ; height: auto; } .controls { margin - top: 10px; display: flex; gap: 10px; flex - wrap: wrap; } button { padding: 5px 10px; cursor: pointer; }
為了進(jìn)一步完善我們的簽名板功能,script.js
腳本需要進(jìn)行以下更新:
- 實(shí)現(xiàn)兩個(gè)棧,
undoStack
和redoStack
,分別用來(lái)追蹤撤銷和重做的歷史狀態(tài)。 - 創(chuàng)建一個(gè)
saveState
函數(shù),該函數(shù)負(fù)責(zé)將簽名板的當(dāng)前狀態(tài)存儲(chǔ)到撤銷棧中。 - 定義
undo
和redo
函數(shù),分別用于執(zhí)行撤銷和重做的操作。
經(jīng)過(guò)這些修改,script.js
腳本將具備完整的撤銷和重做功能。以下是更新后的 script.js
腳本:
document.addEventListener('DOMContentLoaded', function() { var canvas = document.getElementById('signature-pad'); var signaturePad = new SignaturePad(canvas); var undoStack = []; var redoStack = []; function saveState() { undoStack.push(deepCopy(signaturePad.toData())); redoStack = []; } function undo() { if (undoStack.length > 0) { redoStack.push(deepCopy(signaturePad.toData())); undoStack.pop(); signaturePad.clear(); if (undoStack.length) { var lastStroke = undoStack[undoStack.length - 1]; signaturePad.fromData(lastStroke, { clear: false }); } } } function redo() { if (redoStack.length > 0) { undoStack.push(deepCopy(signaturePad.toData())); var nextState = redoStack.pop(); signaturePad.clear(); if (nextState.length) { signaturePad.fromData(nextState); } } } document.getElementById('undo').addEventListener('click', undo); document.getElementById('redo').addEventListener('click', redo); document.getElementById('clear').addEventListener('click', function() { signaturePad.clear(); undoStack = []; redoStack = []; }); document.getElementById('save-png').addEventListener('click', function() { if (!signaturePad.isEmpty()) { var dataURL = signaturePad.toDataURL('image/png'); var link = document.createElement('a'); link.href = dataURL; link.download = 'signature.png'; link.click(); } }); document.getElementById('save-jpeg').addEventListener('click', function() { if (!signaturePad.isEmpty()) { var dataURL = signaturePad.toDataURL('image/jpeg'); var link = document.createElement('a'); link.href = dataURL; link.download = 'signature.jpeg'; link.click(); } }); // 繪圖結(jié)束時(shí)保存狀態(tài) signaturePad.addEventListener("endStroke", () = >{ console.log("Signature end"); saveState(); }); // 初始畫(huà)布設(shè)置 function resizeCanvas() { var ratio = Math.max(window.devicePixelRatio || 1, 1); canvas.width = canvas.offsetWidth * ratio; canvas.height = canvas.offsetHeight * ratio; canvas.getContext('2d').scale(ratio, ratio); signaturePad.clear(); // 否則 isEmpty() 可能會(huì)返回錯(cuò)誤值 if (undoStack.length > 0) { signaturePad.fromData(undoStack[undoStack.length - 1]); } } function deepCopy(data) { return JSON.parse(JSON.stringify(data)); } window.addEventListener('resize', resizeCanvas); resizeCanvas(); });
應(yīng)用場(chǎng)景介紹:JavaScript 簽名板
掌握上述代碼能夠讓我們應(yīng)對(duì)多種實(shí)際應(yīng)用場(chǎng)景。
電子簽名在網(wǎng)頁(yè)表單中的應(yīng)用
通過(guò)在網(wǎng)頁(yè)表單中集成簽名板,用戶可以輕松地電子簽名文檔,這在處理合同、協(xié)議和同意書(shū)時(shí)尤為重要。在法律、房地產(chǎn)、醫(yī)療和金融等行業(yè)內(nèi),電子簽名的收集成為工作流程中不可或缺的一環(huán),有效減少了紙質(zhì)文件的使用。
在線繪圖工具的集成
開(kāi)發(fā)者可以將簽名板整合到需要用戶進(jìn)行繪圖或注釋的在線應(yīng)用中。無(wú)論是協(xié)作白板、設(shè)計(jì)軟件還是交互式反饋環(huán)節(jié),簽名板都能發(fā)揮重要作用。例如,在在線教育平臺(tái)上,學(xué)生可以利用簽名板在直播課程中繪制圖表或解答數(shù)學(xué)題目,教師也能即時(shí)給予反饋。
網(wǎng)絡(luò)文檔和圖像編輯器中的注釋功能
簽名板可作為網(wǎng)絡(luò)文檔和圖像編輯器中的注釋工具,讓用戶能夠直接在文檔或圖像上添加手寫(xiě)注釋、評(píng)論或草圖。
訪客登記管理
在訪客登記管理中,簽名板可用于記錄訪客的數(shù)字化簽到和簽出信息,這在企業(yè)管理和活動(dòng)策劃中非常實(shí)用。
總結(jié)
在本篇文章中,我們探索了如何使用純 JavaScript 來(lái)創(chuàng)建一個(gè)功能豐富的簽名板,包括觸摸支持、筆觸樣式選擇、響應(yīng)式設(shè)計(jì)以及導(dǎo)出功能。我們學(xué)習(xí)了如何利用 HTML 的 <canvas>
元素來(lái)捕捉用戶的手寫(xiě)簽名,并通過(guò) JavaScript 為簽名板添加了撤銷、重做等高級(jí)功能。此外,我們還通過(guò)引入 signature_pad
庫(kù)進(jìn)一步簡(jiǎn)化了開(kāi)發(fā)過(guò)程,使得實(shí)現(xiàn)平滑的簽名體驗(yàn)變得更加容易。
通過(guò)這些技術(shù),我們不僅能夠創(chuàng)建一個(gè)基本的簽名板,還能夠?yàn)槠涮砑痈鞣N實(shí)用的功能,使其適應(yīng)不同的應(yīng)用場(chǎng)景。無(wú)論是在網(wǎng)頁(yè)表單中收集電子簽名,還是在在線教育平臺(tái)中作為繪圖工具,或是作為網(wǎng)絡(luò)文檔編輯器中的注釋工具,JavaScript 簽名板都能提供出色的用戶體驗(yàn)。
此外,我們還討論了如何將簽名數(shù)據(jù)導(dǎo)出為不同格式的圖片,這對(duì)于保存簽名或?qū)⑵浒l(fā)送到服務(wù)器進(jìn)行進(jìn)一步處理非常有用。例如,簽名驗(yàn)證功能可以提高在線交易的安全性,確保用戶的身份得到確認(rèn)。
總的來(lái)說(shuō),JavaScript 簽名板的靈活性和可定制性使其成為現(xiàn)代網(wǎng)絡(luò)應(yīng)用的理想選擇。隨著技術(shù)的不斷進(jìn)步,我們可以預(yù)見(jiàn),JavaScript 簽名板將在未來(lái)的網(wǎng)絡(luò)應(yīng)用中扮演越來(lái)越重要的角色,為用戶提供更加豐富和便捷的手寫(xiě)簽名體驗(yàn)。通過(guò)本文的指導(dǎo),開(kāi)發(fā)者可以輕松地在自己的項(xiàng)目中實(shí)現(xiàn)這些功能,無(wú)論是簡(jiǎn)單的簽名收集還是復(fù)雜的交互式繪圖和注釋,都能輕松應(yīng)對(duì)。這種技術(shù)的應(yīng)用不僅提高了工作效率,也為用戶帶來(lái)了更加直觀和個(gè)性化的交互方式。
以上就是JavaScript實(shí)現(xiàn)簽名板功能的詳細(xì)內(nèi)容,更多關(guān)于JavaScript簽名板的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
20個(gè)常見(jiàn)的JavaScript數(shù)組操作總結(jié)
JavaScript中的Array對(duì)象與其他編程語(yǔ)言中的數(shù)組一樣,是一組數(shù)據(jù)的集合。在JavaScript中,數(shù)組里面的數(shù)據(jù)可以是不同類型的,并具有用于執(zhí)行數(shù)組常見(jiàn)操作的方法,本文整理了一些常用的,需要的可以參考一下2022-09-09JavaScript 原型繼承之構(gòu)造函數(shù)繼承
JavaScript 是基于原型的面向?qū)ο笳Z(yǔ)言。也就是說(shuō),每個(gè)實(shí)例對(duì)象都具有一個(gè)原型。對(duì)象從該原型中繼承屬性和方法。這一篇將具體說(shuō)說(shuō)構(gòu)造函數(shù)的繼承。2011-08-08基于Bootstrap模態(tài)對(duì)話框只加載一次 remote 數(shù)據(jù)的解決方法
下面小編就為大家?guī)?lái)一篇基于Bootstrap模態(tài)對(duì)話框只加載一次 remote 數(shù)據(jù)的解決方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07用Js實(shí)現(xiàn)的動(dòng)態(tài)增加表格示例自己寫(xiě)的
動(dòng)態(tài)增加表格的方法有很多,但大多說(shuō)實(shí)現(xiàn)起來(lái)比較繁瑣,本文的這個(gè)示例是作者自己手寫(xiě)的,經(jīng)測(cè)試效果還不錯(cuò),但唯獨(dú)不兼容FF,感興趣的朋友可以參考下2013-10-10web前端開(kāi)發(fā)upload上傳頭像js示例代碼
這篇文章主要為大家詳細(xì)介紹了web前端開(kāi)發(fā)upload上傳頭像js示例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10