JavaScript實(shí)現(xiàn)簽名板功能
簽名板在網(wǎng)頁應(yīng)用中扮演著重要角色,它們能夠記錄用戶的手寫簽名或者繪圖,從而提升了用戶體驗(yàn)。
在接下來的內(nèi)容中,我將指導(dǎo)你如何利用 JavaScript 來開發(fā)一個(gè)功能豐富的簽名板。這個(gè)簽名板不僅支持自定義和響應(yīng)式設(shè)計(jì),還具備對(duì)觸摸設(shè)備的兼容、多樣的筆觸樣式選擇,以及將簽名導(dǎo)出為不同格式圖片的能力。此外,我們還將探索如何整合像 signature_pad 這樣的先進(jìn)工具來增強(qiáng)簽名板的功能。
開始
讓我們使用純 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)簽來構(gòu)建簽名板。選擇畫布作為工具是因?yàn)樗鼮槲覀兲峁┝艘韵鹿δ埽?/p>
- 利用 JavaScript 實(shí)現(xiàn)自由手寫,這對(duì)于獲取簽名是必不可少的。
- 對(duì)畫布的外觀和行為進(jìn)行個(gè)性化定制,包括線條的顏色、粗細(xì)以及風(fēng)格。
- 兼容多樣的鼠標(biāo)和觸摸事件,捕捉用戶在繪圖、移動(dòng)以及提筆或松手時(shí)的互動(dòng)。
- 利用 toDataURL 方法將簽名以圖像形式導(dǎo)出(如 PNG 或 JPEG),這在保存簽名或?qū)⑵渖蟼髦练?wù)器時(shí)非常有用。
接下來,我們將通過 styles.css 為這個(gè)頁面添加一些樣式,以提升其視覺效果。
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ì)解釋一下這段代碼的工作原理:
- 我們首先通過
canvas變量來綁定到頁面上的<canvas>元素。 - 接著,
ctx變量用于獲取該畫布的 2D 渲染上下文,它包含了繪制圖形所需的各種方法和屬性。 - 當(dāng)用戶按下鼠標(biāo)按鈕時(shí),我們通過一個(gè)
mousedown事件監(jiān)聽器來觸發(fā)繪圖動(dòng)作。此時(shí),ctx.beginPath()用于開啟新的繪圖路徑,而ctx.moveTo(e.offsetX, e.offsetY)則將畫筆定位到鼠標(biāo)點(diǎn)擊的準(zhǔn)確位置。 - 用戶移動(dòng)鼠標(biāo)時(shí),
mousemove事件監(jiān)聽器會(huì)根據(jù)鼠標(biāo)的當(dāng)前位置繪制線條。ctx.lineTo(e.offsetX, e.offsetY)命令畫筆繪制一條直線到新位置,隨后ctx.stroke()將這條線實(shí)際畫到畫布上。 - 為了結(jié)束繪圖,我們?cè)O(shè)置了
mouseup和mouseout兩個(gè)事件監(jiān)聽器,分別在鼠標(biāo)按鈕釋放和鼠標(biāo)光標(biāo)移出畫布時(shí)停止繪圖。此外,當(dāng)用戶點(diǎn)擊清除按鈕時(shí),ctx.clearRect(0, 0, canvas.width, canvas.height);用于清除整個(gè)畫布,為新的繪圖做準(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 來獲取觸摸坐標(biāo)。為了考慮畫布的位置,使用 canvas.offsetLeft 和 canvas.offsetTop 進(jìn)行調(diào)整:
自定義
讓我們給簽名板增添一些新功能,比如讓用戶能夠挑選不同的筆觸效果。為此,我們將執(zhí)行以下關(guān)鍵步驟:
- 在 HTML 代碼中嵌入一個(gè)
<select>標(biāo)簽,它將包含兩個(gè)選項(xiàng):Pen和Brush,從而讓用戶可以挑選他們喜歡的筆觸風(fēng)格。 - 為這些新增的控件編寫相應(yīng)的 CSS 樣式,確保它們?cè)陧撁嫔险_地顯示。
- 為下拉選擇菜單設(shè)置一個(gè)事件監(jiān)聽器,以便在用戶選擇不同的選項(xiàng)時(shí),動(dòng)態(tài)地改變筆觸樣式。
- 根據(jù)用戶所選擇的筆觸樣式,調(diào)整
ctx.lineWidth(線寬)和ctx.lineCap(線帽形狀)的值,以反映不同的視覺效果。
以下是需要添加到 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è)置初始筆畫樣式
ctx.lineWidth = 2;
ctx.lineCap = 'round';
});
處理響應(yīng)性
當(dāng)屏幕縮小時(shí),簽名板可能因?yàn)槿鄙夙憫?yīng)式設(shè)計(jì)而無法正常工作。為了改善這一點(diǎn),我們可以增強(qiáng)應(yīng)用的響應(yīng)性,使其在小屏幕設(shè)備上也能輕松進(jìn)行簽名操作。
關(guān)鍵在于確保畫布及其容器能夠靈活適應(yīng)不同的屏幕尺寸。這需要我們根據(jù)瀏覽器窗口的大小來動(dòng)態(tài)調(diào)整畫布的尺寸。
具體來說,我們需要做出以下調(diào)整:
- 為
.signature-container設(shè)置一個(gè)靈活的寬度,比如占滿90%的可用空間,并設(shè)定一個(gè)最大寬度限制,比如600像素。 - 將畫布元素的寬度設(shè)置為
width: 100%,高度設(shè)置為height: auto,以實(shí)現(xiàn)響應(yīng)式效果。
在 JavaScript 代碼中,我們需要執(zhí)行以下操作:
- 創(chuàng)建一個(gè)名為
resizeCanvas的函數(shù),用以根據(jù)其容器尺寸的變化來調(diào)整畫布的大小。 - 在頁面加載時(shí)首先調(diào)用
resizeCanvas函數(shù),并在窗口大小發(fā)生變化時(shí)添加一個(gè)事件監(jiān)聽器,確保畫布能夠隨著窗口大小的變化而動(dòng)態(tài)調(diào)整尺寸。
按照上述說明,我們需要更新 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;
});
// 初始畫布設(shè)置
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
});
你可能會(huì)觀察到,當(dāng)改變?yōu)g覽器窗口大小時(shí),畫布上的簽名會(huì)消失,這是因?yàn)檎{(diào)整畫布尺寸會(huì)自動(dòng)清除其內(nèi)容。這是 <canvas> 元素的標(biāo)準(zhǔn)行為。不過,我們可以通過一些技巧來避免這個(gè)問題。
為了在縮放畫布時(shí)保持簽名的完整性,并且能夠正確地對(duì)其進(jìn)行縮放,我們需要先保存畫布上的簽名,然后在調(diào)整畫布尺寸后再重新繪制它。我們可以通過 toDataURL 方法來保存畫布上的簽名。
HTMLCanvasElement.toDataURL() 方法能夠根據(jù)你指定的圖片格式,將畫布上的內(nèi)容轉(zhuǎn)換成一個(gè)數(shù)據(jù) URL。
現(xiàn)在,讓我們來看一下如何保存簽名,以便在窗口大小調(diào)整后可以將其恢復(fù)。我們需要做的調(diào)整如下:
- 定義一個(gè)
signatureData變量來存儲(chǔ)簽名的當(dāng)前狀態(tài),以數(shù)據(jù) URL 的形式。 - 修改
resizeCanvas函數(shù),使其能在調(diào)整畫布大小時(shí),先保存簽名,再調(diào)整尺寸,并最終重新繪制簽名。 - 創(chuàng)建一個(gè)
Image對(duì)象來加載保存的簽名,并在調(diào)整尺寸后的畫布上將其恢復(fù)。 - 當(dāng)完成繪畫時(shí),使用
canvas.toDataURL()方法更新signatureData變量,以便存儲(chǔ)當(dāng)前的畫布內(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();
});
// 初始畫布設(shè)置
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
});
保存和導(dǎo)出
我們將進(jìn)一步增強(qiáng)功能,允許用戶將他們的簽名以 PNG 或 JPEG 格式導(dǎo)出,且這些圖片將擁有白色背景。為此,我們將執(zhí)行以下步驟:
- 在 HTML 代碼中增加兩個(gè)按鈕,分別用于將簽名導(dǎo)出為 PNG 和 JPEG 文件。
- 開發(fā)一個(gè)名為
exportCanvas的函數(shù),該函數(shù)負(fù)責(zé)將畫布內(nèi)容(包括白色背景)導(dǎo)出。這一過程涉及到創(chuàng)建一個(gè)新畫布,將白色填充為背景色,然后將當(dāng)前簽名繪制上去,最后將其保存為 PNG 或 JPEG 文件。
以下是經(jīng)過這些更改后的 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)出畫布
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');
});
// 初始畫布設(shè)置
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
});
添加更多功能
通過引入外部庫,我們可以簡(jiǎn)化簽名板中一些復(fù)雜功能的實(shí)現(xiàn)過程。我們將采用 signature_pad 這個(gè)庫,它非常出色,能夠幫助我們輕松地實(shí)現(xiàn)之前討論過的眾多功能,并且它還能讓我們生成更加流暢的簽名效果。
首先,我們需要在 HTML 文檔中引入 signature_pad 庫:
<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,分別用來追蹤撤銷和重做的歷史狀態(tài)。 - 創(chuàng)建一個(gè)
saveState函數(shù),該函數(shù)負(fù)責(zé)將簽名板的當(dāng)前狀態(tài)存儲(chǔ)到撤銷棧中。 - 定義
undo和redo函數(shù),分別用于執(zhí)行撤銷和重做的操作。
經(jīng)過這些修改,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();
});
// 初始畫布設(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īng)用
通過在網(wǎng)頁表單中集成簽名板,用戶可以輕松地電子簽名文檔,這在處理合同、協(xié)議和同意書時(shí)尤為重要。在法律、房地產(chǎn)、醫(yī)療和金融等行業(yè)內(nèi),電子簽名的收集成為工作流程中不可或缺的一環(huán),有效減少了紙質(zhì)文件的使用。
在線繪圖工具的集成
開發(fā)者可以將簽名板整合到需要用戶進(jìn)行繪圖或注釋的在線應(yīng)用中。無論是協(xié)作白板、設(shè)計(jì)軟件還是交互式反饋環(huán)節(jié),簽名板都能發(fā)揮重要作用。例如,在在線教育平臺(tái)上,學(xué)生可以利用簽名板在直播課程中繪制圖表或解答數(shù)學(xué)題目,教師也能即時(shí)給予反饋。
網(wǎng)絡(luò)文檔和圖像編輯器中的注釋功能
簽名板可作為網(wǎng)絡(luò)文檔和圖像編輯器中的注釋工具,讓用戶能夠直接在文檔或圖像上添加手寫注釋、評(píng)論或草圖。
訪客登記管理
在訪客登記管理中,簽名板可用于記錄訪客的數(shù)字化簽到和簽出信息,這在企業(yè)管理和活動(dòng)策劃中非常實(shí)用。
總結(jié)
在本篇文章中,我們探索了如何使用純 JavaScript 來創(chuàng)建一個(gè)功能豐富的簽名板,包括觸摸支持、筆觸樣式選擇、響應(yīng)式設(shè)計(jì)以及導(dǎo)出功能。我們學(xué)習(xí)了如何利用 HTML 的 <canvas> 元素來捕捉用戶的手寫簽名,并通過 JavaScript 為簽名板添加了撤銷、重做等高級(jí)功能。此外,我們還通過引入 signature_pad 庫進(jìn)一步簡(jiǎn)化了開發(fā)過程,使得實(shí)現(xiàn)平滑的簽名體驗(yàn)變得更加容易。
通過這些技術(shù),我們不僅能夠創(chuàng)建一個(gè)基本的簽名板,還能夠?yàn)槠涮砑痈鞣N實(shí)用的功能,使其適應(yīng)不同的應(yīng)用場(chǎng)景。無論是在網(wǎng)頁表單中收集電子簽名,還是在在線教育平臺(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)。
總的來說,JavaScript 簽名板的靈活性和可定制性使其成為現(xiàn)代網(wǎng)絡(luò)應(yīng)用的理想選擇。隨著技術(shù)的不斷進(jìn)步,我們可以預(yù)見,JavaScript 簽名板將在未來的網(wǎng)絡(luò)應(yīng)用中扮演越來越重要的角色,為用戶提供更加豐富和便捷的手寫簽名體驗(yàn)。通過本文的指導(dǎo),開發(fā)者可以輕松地在自己的項(xiàng)目中實(shí)現(xiàn)這些功能,無論是簡(jiǎn)單的簽名收集還是復(fù)雜的交互式繪圖和注釋,都能輕松應(yīng)對(duì)。這種技術(shù)的應(yīng)用不僅提高了工作效率,也為用戶帶來了更加直觀和個(gè)性化的交互方式。
以上就是JavaScript實(shí)現(xiàn)簽名板功能的詳細(xì)內(nèi)容,更多關(guān)于JavaScript簽名板的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
20個(gè)常見的JavaScript數(shù)組操作總結(jié)
JavaScript中的Array對(duì)象與其他編程語言中的數(shù)組一樣,是一組數(shù)據(jù)的集合。在JavaScript中,數(shù)組里面的數(shù)據(jù)可以是不同類型的,并具有用于執(zhí)行數(shù)組常見操作的方法,本文整理了一些常用的,需要的可以參考一下2022-09-09
JavaScript 原型繼承之構(gòu)造函數(shù)繼承
JavaScript 是基于原型的面向?qū)ο笳Z言。也就是說,每個(gè)實(shí)例對(duì)象都具有一個(gè)原型。對(duì)象從該原型中繼承屬性和方法。這一篇將具體說說構(gòu)造函數(shù)的繼承。2011-08-08
基于Bootstrap模態(tài)對(duì)話框只加載一次 remote 數(shù)據(jù)的解決方法
下面小編就為大家?guī)硪黄贐ootstrap模態(tài)對(duì)話框只加載一次 remote 數(shù)據(jù)的解決方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-07-07
用Js實(shí)現(xiàn)的動(dòng)態(tài)增加表格示例自己寫的
動(dòng)態(tài)增加表格的方法有很多,但大多說實(shí)現(xiàn)起來比較繁瑣,本文的這個(gè)示例是作者自己手寫的,經(jīng)測(cè)試效果還不錯(cuò),但唯獨(dú)不兼容FF,感興趣的朋友可以參考下2013-10-10

