开源分享: 基于vue3的电子签名组件

时间:2025-03-28 11:34:44
  • <script lang="ts" setup>
  • import { ref, watch, onMounted,onUnmounted } from "vue";
  • interface IProps {
  •     /**
  •      * @description   画布宽度
  •      * @default       400
  •      */
  •      width?: number;
  •      /**
  •       * @description   画布高度
  •       * @default       200
  •       */
  •      height?: number;
  •      /**
  •       * @description   线宽
  •       * @default       4
  •      */
  •      lineWidth?: number;
  •      /**
  •       * @description   线段颜色
  •       * @default       'red'
  •      */
  •      strokeColor?: string;
  •      /**
  •       * @description   设置线条两端圆角
  •       * @default       'round'
  •      */
  •      lineCap?: string;
  •      /**
  •       * @description   线条交汇处圆角
  •       * @default       'round'
  •      */
  •      lineJoin?: string;
  •      /**
  •       * @description   画布背景颜色
  •       * @default       'transparent'
  •      */
  •      bgColor?: string;
  •      /**
  •       * @description   true
  •      */
  •      showBtn?: boolean;
  •      /**
  •      * @description   当保存时的回调, blob为生成的图片bob
  •      * @default       -
  •      */
  •      onSave?: (blob: Blob) => void;
  •     /**
  •      * @description   当画布清空时的回调, 参数为画布的上下文对象,可以直接使用canvas的api
  •      * @default       -
  •      */
  •      onClear?: (canvasContext: CanvasRenderingContext2D) => void;
  •      /**
  •      * @description   当画布结束时的回调
  •      * @default       -
  •      */
  •      onDrawEnd?: (canvas: HTMLCanvasElement) => void;
  •   }
  • const props = withDefaults(defineProps<IProps>(), {
  •   width: 400,
  •   height: 200,
  •   lineWidth:4,
  •   strokeColor:'green',
  •   lineCap:'round',
  •   lineJoin:'round',
  •   bgColor:'transparent',
  •   showBtn:true
  • });
  • const {
  •   width,
  •   height,
  •   lineWidth,
  •   strokeColor,
  •   lineCap,
  •   lineJoin,
  •   bgColor,
  •   showBtn,
  •   onSave,
  •   onClear,
  •   onDrawEnd
  • } = props;
  •    const canvasRef = ref<any>(null);
  •     const ctxRef = ref<any>(null);
  •    // 保存上次绘制的 坐标及偏移量
  •    const client = ref<any>({
  •               offsetX: 0// 偏移量
  •               offsetY: 0,
  •               endX: 0// 坐标
  •               endY: 0
  •           })
  •   
  •  // 判断是否为移动端
  •  const mobileStatus = (/Mobile|Android|iPhone/());
  •    // 取消-清空画布
  •    const cancel = () => {
  •     // 清空当前画布上的所有绘制内容
  •     if() {
  •       const canvasCtx = ("2d");
  •       (00, width, height);
  •       
  •       onClear && onClear()
  •     }
  •   }
  •   // 保存-将画布内容保存为图片
  •   const save = () => {
  •     // 将canvas上的内容转成blob流
  •     ((blob: any) => {
  •         // 获取当前时间并转成字符串,用来当做文件名
  •         const date = ().toString()
  •         // 创建一个 a 标签
  •         const a = ('a')
  •         // 设置 a 标签的下载文件名
  •          = `${date}.png`
  •         // 设置 a 标签的跳转路径为 文件流地址
  •          = (blob)
  •         // 手动触发 a 标签的点击事件
  •         ()
  •         // 移除 a 标签
  •         ()
  •         onSave && onSave(blob);
  •     })
  •   }
  •    // 绘制
  •    const draw = (event: { changedTouches?: any; pageX?: any; pageY?: any; }) => {
  •         // 获取当前坐标点位
  •         const { pageX, pageY } = mobileStatus ? [0] : event
  •         // 获取canvas 实例
  •         const canvas:HTMLCanvasElement =  as any;
  •         
  •         const { x, y } = ();
  •         // 修改最后一次绘制的坐标点
  •          = pageX
  •          = pageY
  •         // 根据坐标点位移动添加线条
  •         (pageX - x, pageY - y)
  •         // 绘制
  •          .stroke()
  •     };
  •    // 初始化
  •    const init = (event: { changedTouches?: any; offsetX?: any; offsetY?: any; pageX?: any; pageY?: any; }) => {
  •         // 获取偏移量及坐标
  •         const { offsetX, offsetY, pageX, pageY } = mobileStatus ? [0] : event;
  •         const canvas:HTMLCanvasElement =  as any;
  •         const { x, y } = ();
  •      
  •          = offsetX
  •          = offsetY
  •          = pageX
  •          = pageY
  •         // 清除以上一次 beginPath 之后的所有路径,进行绘制
  •         ()
  •         // 根据配置文件设置相应配置
  •          = lineWidth
  •          = strokeColor
  •          = lineCap
  •          = lineJoin
  •         // 设置画线起始点位
  •         ( - x,  - y)
  •         // 监听 鼠标移动或手势移动
  •         (mobileStatus ? "touchmove" : "mousemove", draw)
  •     };
  •   // 结束绘制
  •   const closeDraw = () => {
  •          ();
  •         // 结束绘制
  •         ()
  •         // 移除鼠标移动或手势移动监听器
  •         ("mousemove", draw)
  •         onDrawEnd && onDrawEnd()
  •     };
  •   const initCanvas =()=>{
  •        // 获取canvas 实例
  •        const canvas:HTMLCanvasElement =  as any;
  •           // 设置宽高
  •            = width;
  •            = height;
  •           // 创建上下文
  •           const ctx:any = ('2d');
  •            = ctx;
  •           // 设置填充背景色
  •            = bgColor;
  •           // 绘制填充矩形
  •           (
  •               0// x 轴起始绘制位置
  •               0// y 轴起始绘制位置
  •               width, // 宽度
  •               height // 高度
  •           );
  •   }
  •   const  addEventListener=()=>{
  •      // 创建鼠标/手势按下监听器
  •      (mobileStatus ? "touchstart" : "mousedown", init);
  •       // 创建鼠标/手势 弹起/离开 监听器
  •     (mobileStatus ? "touchend" : "mouseup", closeDraw);
  •     
  •   }
  •   const  removeEventListener=()=>{
  •      // 创建鼠标/手势按下监听器
  •      (mobileStatus ? "touchstart" : "mousedown", init);
  •       // 创建鼠标/手势 弹起/离开 监听器
  •     (mobileStatus ? "touchend" : "mouseup", closeDraw);
  •     
  •   }
  •   
  • const initEsign=()=>{
  •      initCanvas();
  •      addEventListener();
  •     
  •   }
  •   onMounted(() => {
  •     initEsign();
  • });
  • onUnmounted(()=>{
  •   removeEventListener();
  • });
  • </script>