preloader
心得

Html Canvas Signature Location Shift Experience Resolve | HTML Canvas 簽名位置偏移的解決方法

Html Canvas Signature Location Shift Experience Resolve | HTML Canvas 簽名位置偏移的解決方法

使用 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)
    }
  }


}

參考資料:

  1. increase-html5-canvas-resolution@stackoverflow
  2. Canvas maximums size@MDN