使用 HTML 的 canvas
標籤作為手寫簽名功能的實現方式,這在行動裝置(例如智慧型手機)上會遇到筆跡出現處跟下筆處不同的 bug,電腦環境則沒有 bug。如果使用套件的 web component
,很可能元件使用 Shadow DOM
包裝方式,確保自己的樣式不受其他套件樣式干擾。這篇文章目的是提供解決方法。
解決方法: 用 javascript 設定 canvas 樣式、縮放比例和它的 css 寬高樣式。
假設 canvas 是被 @almothafar/angular-signature-pad
套件標籤包住,而且已用 js 設定 <signature-pad></signature-pad>
的寬高尺寸,例如:
<signature-pad id="signature-pad">
<canvas class="signature-canvas">
</canvas>
</signature-pad>
那麼就能使用 js 進一步動態設定 canvas 寬高樣式、縮放比例和 css 寬高樣式。以下是 typescript 語言呈現,請自行轉換成 javascript。
timer: ReturnType<typeof setTimeout>;
/*
* Notice: @HostListener 是 Angular component 聆聽指定事件的方法使用方式。
* 若不是使用 Angular ,可改用 vanilla js 的 AddEventListener() 方式
*/
@HostListener('window:orientationchange')
@HostListener('window:resize')
resize() {
if (this.timer) {
clearTimeout(this.timer)
}
this.timer = setTimeout(() => {
this.prepareSignatureArea()
}, 200)
}
prepareSignatureArea() {
const signaturePadElem = document.getElementById('signature-pad')
const padWidth = signaturePadElem.clientWidth
if (padWidth === 0) {
setTimeout(() => {
this.prepareSignatureArea()
}, 100)
}
const padHeight = clientWidth * 0.48 // Notice: 0.48 只是舉例,請自行調整
// 取得行動裝置的 pixel 比例資訊
const ratio = Math.max(window.devicePixelRatio || 1, 1)
const canvasElem = document.getElementByClass('signature-canvas')
if (canvasElem) {
// 調整 canvas 在 css 畫面尺寸
canvasElem.style.width = padWidth + 'px'
canvasElem.style.height = padHeight + 'px'
// 設定 canvas 實際尺寸
canvasElem.width = width * ratio
canvasElem.height = height * ratio
// 取得 canvas context 與設定縮放
const canvasContext = canvasElem.getContext('2d')
if (canvasContext) {
canvasContext.scale(ratio, ratio)
}
// 建立空白的畫布,並取得空白內容
const emptyCanvasElem = document.createElement('canvas')
emptyCanvasElem.width = canvasElem.width
emptyCanvasElem.height = canvasElem.height
const emptyContext = emptyCanvasElem.getContext('2d')
emptyContext.fillStyle = '#FFFFFF'
emptyContext.fillRect(0, 0, emptyCanvasElem.width, emptyCanvasElem.height)
if (emptyCanvasElem.toDataURL() != canvasElem.toDataURL()) {
// 暫存當下的簽名資料
const data = canvasElem.toDataURL()
const img = new Image()
img.onload = () => {
// 清除原本的內容
canvasContext.clearRect(0, 0, canvasElem.width, canvasElem.height)
// 讀入原本的內容
canvasContext.drawImage(img, 0, 0, canvasElem.width, canvasElem.height)
}
// 設定圖片來源
img.src = data
} else {
// 清除原本的內容
canvasContext.clearRect(0, 0, canvasElem.width, canvasElem.height)
}
}
}
參考資料: