欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue3使用Signature Pad實(shí)現(xiàn)電子簽名功能

 更新時(shí)間:2025年04月11日 11:14:36   作者:zoahxmy0929  
Vue3簽名板是一種在Vue3應(yīng)用中實(shí)現(xiàn)用戶簽名功能的組件,Vue3-signature-pad的實(shí)現(xiàn)旨在為開(kāi)發(fā)者提供一個(gè)簡(jiǎn)單易用的工具,使用戶能夠在前端界面上進(jìn)行簽名操作,常見(jiàn)于電子商務(wù)、電子合同等場(chǎng)景,本文介紹了Vue3使用Signature Pad實(shí)現(xiàn)電子簽名功能,需要的朋友可以參考下

Signature Pad 基礎(chǔ)用法

安裝與引入

首先需要安裝 signature_pad 庫(kù):

npm install signature_pad

然后在Vue組件中引入:

import SignaturePad from "signature_pad";

基本實(shí)現(xiàn)

<template>
  <div>
    <div class="signature-pad-container">
      <canvas ref="signaturePad"></canvas>
    </div>
    <button @click="clear">清除</button>
    <button @click="save">保存</button>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import SignaturePad from 'signature_pad'

const signaturePad = ref(null)
let signaturePadInstance = null

onMounted(() => {
  signaturePadInstance = new SignaturePad(signaturePad.value)
})

const clear = () => {
  signaturePadInstance.clear()
}

const save = () => {
  const dataURL = signaturePadInstance.toDataURL()
  console.log(dataURL) // 輸出簽名圖片的Base64編碼
}
</script>

解決簽名位置偏差問(wèn)題

問(wèn)題根源分析

簽名位置偏差通常由以下原因?qū)е拢?/p>

  • Canvas尺寸與顯示尺寸不匹配Canvaswidth / height 屬性和CSS的 width / height 樣式,兩者需要一致。
  • 設(shè)備像素比(DPR)問(wèn)題:在高分辨率屏幕上,CSS 像素與設(shè)備像素不一致。
  • 坐標(biāo)系未校正:未考慮設(shè)備像素比導(dǎo)致的事件坐標(biāo)轉(zhuǎn)換錯(cuò)誤。

解決方案

1. 獲取設(shè)備像素比(DPR): 設(shè)備像素比(Device Pixel Ratio)表示一個(gè)CSS像素對(duì)應(yīng)多少個(gè)設(shè)備物理像素。

const dpr = window.devicePixelRatio || 1;

2. 正確設(shè)置Canvas尺寸: 確保了 Canvas 內(nèi)部繪制緩沖區(qū)的大小與屏幕顯示大小成正確比例。

const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
canvas.style.width = `${rect.width}px`;
canvas.style.height = `${rect.height}px`;

3. 初始化簽名板并調(diào)整筆跡粗細(xì): 根據(jù) DPI 調(diào)整筆跡粗細(xì),確保在高分辨率設(shè)備上線條不會(huì)顯得過(guò)細(xì)。

const baseWidth = 2;
signaturePadInstance = new SignaturePad(canvas, {
  minWidth: baseWidth * dpr,
  maxWidth: baseWidth * dpr * 2,
  // 其他配置...
});

4. 坐標(biāo)系校正:(關(guān)鍵) 使事件坐標(biāo)與實(shí)際繪制位置匹配。

const ctx = canvas.getContext("2d");
ctx.scale(dpr, dpr);

5. 觸摸設(shè)備支持: 禁用觸摸設(shè)備的默認(rèn)滾動(dòng)行為,確保簽名體驗(yàn)流暢。

canvas.addEventListener("touchstart", preventScroll, { passive: false });
canvas.addEventListener("touchmove", preventScroll, { passive: false });

const preventScroll = (e) => {
  e.preventDefault();
};

6. 響應(yīng)式處理: 監(jiān)聽(tīng)窗口大小變化并重新初始化簽名板,確保在響應(yīng)式布局中正常工作。

const handleResize = () => {
  if (signaturePadInstance) {
    initSignaturePad(); // 重新初始化以適應(yīng)新尺寸
  }
};

onMounted(() => {
  window.addEventListener("resize", handleResize);
});

onUnmounted(() => {
  window.removeEventListener("resize", handleResize);
});

代碼

const initSignaturePad = () => {
  nextTick(() => {
    const canvas = signaturePad.value;
    if (canvas) {
      // 1. 獲取設(shè)備像素比
      const dpr = window.devicePixelRatio || 1;
      
      // 2. 獲取Canvas的實(shí)際顯示尺寸
      const rect = canvas.getBoundingClientRect();
      
      // 3. 設(shè)置Canvas的實(shí)際繪制尺寸(考慮DPI)
      canvas.width = rect.width * dpr;
      canvas.height = rect.height * dpr;
      
      // 4. 設(shè)置Canvas的CSS顯示尺寸(保持與容器一致)
      canvas.style.width = `${rect.width}px`;
      canvas.style.height = `${rect.height}px`;
      
      // 5. 根據(jù)DPI調(diào)整筆跡粗細(xì)
      const baseWidth = 2;
      
      // 6. 初始化簽名板
      signaturePadInstance = new SignaturePad(canvas, {
        backgroundColor: "rgb(255, 255, 255)",
        penColor: "rgb(0, 0, 0)",
        minWidth: baseWidth * dpr,
        maxWidth: baseWidth * dpr * 2,
        throttle: 16 // 節(jié)流控制提高性能
      });
      
      // 7. 調(diào)整坐標(biāo)系以匹配高DPI
      const ctx = canvas.getContext("2d");
      ctx.scale(dpr, dpr);
      
      // 8. 添加觸摸設(shè)備支持
      canvas.addEventListener("touchstart", preventScroll, { passive: false });
      canvas.addEventListener("touchmove", preventScroll, { passive: false });
    }
  });
};

效果

完整代碼

<template>
  <div class="signature-container">
    <h2>電子簽名</h2>
    <div class="signature-pad-container">
      <canvas ref="signaturePad" class="signature-pad"></canvas>
    </div>
    <div class="signature-actions">
      <el-button @click="clearSignature">清除</el-button>
      <el-button type="primary" @click="saveSignature">保存簽名</el-button>
    </div>
    <div v-if="signatureUrl" class="signature-preview">
      <el-image v-if="signatureUrl" :src="signatureUrl"></el-image>
    </div>
  </div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, nextTick } from "vue";
import SignaturePad from "signature_pad";

const signaturePad = ref(null);
let signaturePadInstance = null;
const signatureUrl = ref("");

const initSignaturePad = () => {
  nextTick(() => {
    const canvas = signaturePad.value;
    if (!canvas) return;

    const dpr = window.devicePixelRatio || 1;
    const rect = canvas.getBoundingClientRect();
    
    canvas.width = rect.width * dpr;
    canvas.height = rect.height * dpr;
    canvas.style.width = `${rect.width}px`;
    canvas.style.height = `${rect.height}px`;
    
    const baseWidth = 2;
    
    signaturePadInstance = new SignaturePad(canvas, {
      backgroundColor: "rgb(255, 255, 255)",
      penColor: "rgb(0, 0, 0)",
      minWidth: baseWidth * dpr,
      maxWidth: baseWidth * dpr * 2,
      throttle: 16,
      velocityFilterWeight: 0.7,
      minDistance: 5
    });
    
    const ctx = canvas.getContext("2d");
    ctx.scale(dpr, dpr);
    
    // 觸摸支持
    canvas.addEventListener("touchstart", preventScroll, { passive: false });
    canvas.addEventListener("touchmove", preventScroll, { passive: false });
  });
};

const preventScroll = (e) => {
  e.preventDefault();
};

const clearSignature = () => {
  if (signaturePadInstance) {
    signaturePadInstance.clear();
    signatureUrl.value = "";
  }
};

const saveSignature = () => {
  if (!signaturePadInstance || signaturePadInstance.isEmpty()) {
    ElMessage.warning("請(qǐng)先簽名");
    return;
  }
  
  // 保存高質(zhì)量PNG
  signatureUrl.value = signaturePadInstance.toDataURL('image/png', 1.0);
  ElMessage.success("簽名保存成功");
};

const handleResize = () => {
  if (signaturePadInstance) {
    initSignaturePad();
  }
};

onMounted(() => {
  initSignaturePad();
  window.addEventListener("resize", handleResize);
});

onUnmounted(() => {
  window.removeEventListener("resize", handleResize);
});
</script>
<style scoped>
.signature-container {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

.signature-pad-container {
  position: relative;
  width: 100%;
  height: 400px;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
  margin: 20px 0;
}

.signature-pad {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  touch-action: none;
}

.signature-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-bottom: 20px;
}

.signature-preview {
  margin-top: 20px;
  text-align: center;
}
</style>

以上就是Vue3使用Signature Pad實(shí)現(xiàn)電子簽名功能的詳細(xì)內(nèi)容,更多關(guān)于Vue3 Signature Pad電子簽名的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vue中的計(jì)算屬性和axios基本使用回顧

    Vue中的計(jì)算屬性和axios基本使用回顧

    這篇文章主要介紹了Vue中的計(jì)算屬性和axios基本使用回顧,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • VUE-PDF實(shí)現(xiàn)pdf在線預(yù)覽問(wèn)題

    VUE-PDF實(shí)現(xiàn)pdf在線預(yù)覽問(wèn)題

    這篇文章主要介紹了VUE-PDF實(shí)現(xiàn)pdf在線預(yù)覽問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • vue實(shí)現(xiàn)表格數(shù)據(jù)的增刪改查

    vue實(shí)現(xiàn)表格數(shù)據(jù)的增刪改查

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)表格數(shù)據(jù)的增刪改查,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • 一篇文章帶你徹底搞懂VUE響應(yīng)式原理

    一篇文章帶你徹底搞懂VUE響應(yīng)式原理

    這篇文章主要介紹了一篇文章帶你徹底搞懂VUE響應(yīng)式原理,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可任意參考一下,需要的朋友可以參考下
    2022-06-06
  • vue3中遇到reactive響應(yīng)式失效的問(wèn)題記錄

    vue3中遇到reactive響應(yīng)式失效的問(wèn)題記錄

    這篇文章主要介紹了vue3中遇到reactive響應(yīng)式失效的問(wèn)題記錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • vue?動(dòng)態(tài)style?拼接寬度問(wèn)題

    vue?動(dòng)態(tài)style?拼接寬度問(wèn)題

    這篇文章主要介紹了vue?動(dòng)態(tài)style?拼接寬度問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • 基于vue.js實(shí)現(xiàn)分頁(yè)查詢功能

    基于vue.js實(shí)現(xiàn)分頁(yè)查詢功能

    這篇文章主要為大家詳細(xì)介紹了基于vue.js實(shí)現(xiàn)分頁(yè)查詢功能,vue.js實(shí)現(xiàn)數(shù)據(jù)庫(kù)分頁(yè),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • 解決vue單頁(yè)面應(yīng)用中動(dòng)態(tài)修改title問(wèn)題

    解決vue單頁(yè)面應(yīng)用中動(dòng)態(tài)修改title問(wèn)題

    這篇文章主要介紹了解決vue單頁(yè)面應(yīng)用中動(dòng)態(tài)修改title問(wèn)題,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-06-06
  • VUE +Element 實(shí)現(xiàn)多個(gè)字段值拼接功能

    VUE +Element 實(shí)現(xiàn)多個(gè)字段值拼接功能

    這篇文章主要介紹了VUE +Element 實(shí)現(xiàn)多個(gè)字段值拼接,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-04-04
  • Vue3?路由配置與導(dǎo)航實(shí)戰(zhàn)教程

    Vue3?路由配置與導(dǎo)航實(shí)戰(zhàn)教程

    Vue?Router?提供了強(qiáng)大的路由管理能力,幫助開(kāi)發(fā)者輕松構(gòu)建流暢、高效的單頁(yè)應(yīng)用,本文將帶你深入探討?Vue3?中的路由配置與導(dǎo)航操作,從安裝到實(shí)戰(zhàn),手把手教你掌握?Vue?Router?的使用技巧,需要的朋友可以參考下
    2025-03-03

最新評(píng)論