Open source sharing: electronic signature component based on vue3

b43d3dde83a60ba93fd622fc6081bebb.png

github address: https://github.com/open-vue3/vue3-sign

Hello, everyone, I'm Xu Xiaoxi, and it's time to share again today. Before I shared with you my open source lightweight electronic signature component—react-sign2 . Today I will continue to share with you the version of the electronic signature component 极客恰恰contributed by my friends .vue3vue3-sign

aff0295aa60fa35077847665634557f6.gif

We can use it to easily implement electronic signatures, such as commonly used contract signatures , manuscript signatures , art signatures, etc., and support one-click saving signatures as local pictures.

Introduction to basic attributes

fdbcc6c34e6b7dff32a2abe60d15b35f.png

event

e7ee34c74f1712dbd85b76bdbe257309.png

Implementation ideas

According to the author's previous habits, before designing components, the design requirements of the components will be clarified first , and then the components will be implemented according to the design principles of robust components. Here I would like to share with you some of my experience in component design:

  • Carry out strict attribute design on components to ensure that the business layer can use components at low cost and maintain a certain degree of configurability

  • Component internal and external type conventions (ts specification), and provide logic compatibility

  • Readability (unified and clear code format, complete comments, clear code structure, and proper use of programming paradigms)

  • Usability (complete code function, good compatibility in different scenarios, business logic coverage)

  • Reusability (code can be reused by other business modules)

  • Maintainability (code is easy to maintain and extend, with some downward/upward compatibility)

  • High performance (components have certain performance, such as rendering of complex scenes, calculation, etc.)

For the electronic signature component, our minimum requirement is to meet the user's online signature and save the signature data .

5c2355973a01b69ca84577b18b0e1428.png

Implementation code

Due to vue3the implementation of the code, here is the main branch of the core jsimplementation, the detailed code can refer to github: https://github.com/open-vue3/vue3-sign.

<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/i.test(navigator.userAgent));

   // 取消-清空画布
   const cancel = () => {
    // 清空当前画布上的所有绘制内容
    if(canvasRef.value) {
      const canvasCtx = canvasRef.value.getContext("2d");
      canvasCtx.clearRect(0, 0, width, height);
      
      onClear && onClear(canvasRef.value)
    }
  }

  // 保存-将画布内容保存为图片
  const save = () => {
    // 将canvas上的内容转成blob流
    canvasRef.value.toBlob((blob: any) => {
        // 获取当前时间并转成字符串,用来当做文件名
        const date = Date.now().toString()
        // 创建一个 a 标签
        const a = document.createElement('a')
        // 设置 a 标签的下载文件名
        a.download = `${date}.png`
        // 设置 a 标签的跳转路径为 文件流地址
        a.href = URL.createObjectURL(blob)
        // 手动触发 a 标签的点击事件
        a.click()
        // 移除 a 标签
        a.remove()

        onSave && onSave(blob);
    })
  }

   // 绘制
   const draw = (event: { changedTouches?: any; pageX?: any; pageY?: any; }) => {
        // 获取当前坐标点位
        const { pageX, pageY } = mobileStatus ? event.changedTouches[0] : event
        // 获取canvas 实例
        const canvas:HTMLCanvasElement = canvasRef.value as any;
        
        const { x, y } = canvas.getBoundingClientRect();
        // 修改最后一次绘制的坐标点
        client.value.endX = pageX
        client.value.endY = pageY
        // 根据坐标点位移动添加线条
        ctxRef.value.lineTo(pageX - x, pageY - y)

        // 绘制
        ctxRef.value .stroke()
    };

   // 初始化
   const init = (event: { changedTouches?: any; offsetX?: any; offsetY?: any; pageX?: any; pageY?: any; }) => {
        // 获取偏移量及坐标
        const { offsetX, offsetY, pageX, pageY } = mobileStatus ? event.changedTouches[0] : event;
        const canvas:HTMLCanvasElement = canvasRef.value as any;

        const { x, y } = canvas.getBoundingClientRect();
     


        client.value.offsetX = offsetX
        client.value.offsetY = offsetY
        client.value.endX = pageX
        client.value.endY = pageY

        // 清除以上一次 beginPath 之后的所有路径,进行绘制
        ctxRef.value.beginPath()
        // 根据配置文件设置相应配置
        ctxRef.value.lineWidth = lineWidth
        ctxRef.value.strokeStyle = strokeColor
        ctxRef.value.lineCap = lineCap
        ctxRef.value.lineJoin = lineJoin
        // 设置画线起始点位
        ctxRef.value.moveTo(client.value.endX - x, client.value.endY - y)
        // 监听 鼠标移动或手势移动
        window.addEventListener(mobileStatus ? "touchmove" : "mousemove", draw)
    };
  // 结束绘制
  const closeDraw = () => {
         console.log(ctxRef.value);
        // 结束绘制
        ctxRef.value.closePath()
        // 移除鼠标移动或手势移动监听器
        window.removeEventListener("mousemove", draw)
        onDrawEnd && onDrawEnd(canvasRef.current)
    };
  const initCanvas =()=>{
       // 获取canvas 实例
       const canvas:HTMLCanvasElement = canvasRef.value as any;
          // 设置宽高
          canvas.width = width;
          canvas.height = height;
          // 创建上下文
          const ctx:any = canvas.getContext('2d');
          ctxRef.value = ctx;
          // 设置填充背景色
          ctxRef.value.fillStyle = bgColor;
          // 绘制填充矩形
          ctxRef.value.fillRect(
              0, // x 轴起始绘制位置
              0, // y 轴起始绘制位置
              width, // 宽度
              height // 高度
          );
  }
  const  addEventListener=()=>{
     // 创建鼠标/手势按下监听器
     window.addEventListener(mobileStatus ? "touchstart" : "mousedown", init);
      // 创建鼠标/手势 弹起/离开 监听器
    window.addEventListener(mobileStatus ? "touchend" : "mouseup", closeDraw);
    
  }
  const  removeEventListener=()=>{
     // 创建鼠标/手势按下监听器
     window.removeEventListener(mobileStatus ? "touchstart" : "mousedown", init);
      // 创建鼠标/手势 弹起/离开 监听器
    window.removeEventListener(mobileStatus ? "touchend" : "mouseup", closeDraw);
    
  }
  
const initEsign=()=>{
     initCanvas();
     addEventListener();
    
  }

  onMounted(() => {
    initEsign();
});

onUnmounted(()=>{
  removeEventListener();
});

</script>

Late planning

1f61b6f9ae2b8a1a49ec02c6d12d6b52.png

Welcome everyone to build together.

References

  1. https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API

  2. https://juejin.cn/post/7174251833773752350


The above is the whole content of this sharing, I hope it will be helpful to you ^_^

If you like it, don't forget to share, like, and bookmark three times~.

913932f00bf8c02b450b68ed73873174.gif

Build a full-stack visual large-screen production platform V6.Dooring from scratch

Build an engine from zero design visualization large screen

Analysis of Data Source Design of Dooring Visual Construction Platform

Some thinking and practice of visual construction

Develop a full-stack document editor from scratch based on Koa + React + TS (advanced practice

Click to see you look the best

Guess you like

Origin blog.csdn.net/KlausLily/article/details/129920177