Front-end implementation of electronic signature (web, mobile) common components

    In the current development of the times, from the previous handwritten signatures, electronic signatures are gradually derived. Electronic signatures have the same legal effect as paper handwritten signatures. At present, electronic signatures are mainly used in product links that require personal confirmation and judicial-related products.

    So how do we implement electronic signatures as the front end? In fact, an important level of auxiliary tags has appeared in html5, that is canvas.

    What is canvas

    Canvas is a new tag in HTML5 that is used to generate images in real time on web pages, and can manipulate image content. Basically, it is a bitmap that can be manipulated by JavaScript. The Canvas object represents an HTML canvas element that supports client-side drawing operations.

    <canvas></canvas>
    


    How to use

    Canvas provides us with a lot of APIs, we only need to create a canvas tag in the body tag, get the node of the canvas tag in the script tag, and create a context to use it

    <body>
         <canvas></canvas>
    </body>
    <script>
         // get the canvas instance
         const canvas = document. querySelector('canvas')
         canvas. getContext('2d')
    </script>
    


    Implement electronic signature

    We only need to get the coordinate point of the current touch and perform line processing.

    1. Add the canvas tag to the body:
    Here we not only need to add the canvas tag in the body, we also need to add two buttons, cancel and save

    <body>
         <canvas></canvas>
         <div>
             <button>Cancel</button>
             <button>Save</button>
         </div>
    </body>
    


    2. Add files

    const config = {
         width: 400, // width
         height: 200, // height
         lineWidth: 5, // line width
         strokeStyle: 'red', // line color
         lineCap: 'round', // set the rounded corners at both ends of the line
         lineJoin: 'round', // rounded corners where lines meet
     }
    


    3. Get the canvas instance

    // get the canvas instance
     const canvas = document. querySelector('canvas')
     // set width and height
     canvas.width = config.width
     canvas.height = config.height
     // Set a frame for us to view and use
     canvas.style.border = '1px solid #000'
     // create context
     const ctx = canvas. getContext('2d')
    


    4. Basic Settings

    We make the fill color of the canvas transparent, and draw and fill a rectangle as our canvas

    // set fill background color
     ctx.fillStyle = 'transparent'
     // draw filled rectangle
     ctx.fillRect(
         0, // x-axis start drawing position
         0, // y-axis starting drawing position
         config.width, // width
         config.height // height
     );
    


    5. Save the last drawn path
    Here we need to declare an object to record the end coordinate point and offset of the path we drew last time.

    Why do we need to save the offset, because there is a certain offset distance between the mouse and the canvas, and we need to subtract this offset in the process of drawing, which is our actual drawing coordinates. But I found that it is not necessary to subtract this offset in chrome, and what you get is the actual coordinates. Before using it in the WeChat applet, you need to subtract the offset. Friends who need to use it in the applet need to pay attention to this.

    // save the last drawn coordinates and offset
     const client = {
         offsetX: 0, // offset
         offsetY: 0,
         endX: 0, // coordinates
         endY: 0
     }
    


    6. Device compatible
    We obtain the current device information by calling navigator.userAgent, and make regular matching judgments

     const mobileStatus = (/Mobile|Android|iPhone/i.test(navigator.userAgent))
    


    7. Initialization
    Here we initialize when the mouse is pressed (mousedown) (web side)/touch start (mobile side), and event monitoring uses addEventListener

    // create mouse/gesture down listener
    window. addEventListener(mobileStatus ? "touchstart" : "mousedown", init)
    

    declare initialization method:
    We add an init method as a callback method to listen for mouse press/touch start.
    Here we need to obtain the offset and coordinates of the current mouse press/touch start, and draw the starting point.

    Tips: The web terminal can be obtained directly from the event, while the mobile terminal needs to be obtained from event.changedTouches[0].

    Here we monitor the movement of the mouse after initialization

    // initialization
     const init = event => {
         // Get the offset and coordinates
         const { offsetX, offsetY, pageX, pageY } = mobileStatus ? event.changedTouches[0] : event
    
         // Modify the last offset and coordinates
         client.offsetX = offsetX
         client.offsetY = offsetY
         client.endX = pageX
         client.endY = pageY
    
         // Clear all paths after the last beginPath and draw
         ctx.beginPath()
    
         // Configure according to the configuration file settings
         ctx.lineWidth = config.lineWidth
         ctx.strokeStyle = config.strokeStyle
         ctx.lineCap = config.lineCap
         ctx.lineJoin = config.lineJoin
    
         // Set the starting point of the line
         ctx.moveTo(client.endX, client.endY)
    
         // Listen for mouse movement or gesture movement
         window. addEventListener(mobileStatus ? "touchmove" : "mousemove", draw)
     }
    


    8. Draw
    Here we add the draw method as a callback method for monitoring mouse movement/touch movement

    // draw
     const draw = event => {
         // Get the current coordinate point
         const { pageX, pageY } = mobileStatus ? event. changedTouches[0] : event
         // Modify the coordinate point of the last drawing
         client.endX = pageX
         client.endY = pageY
    
         // Add lines according to coordinate point movement
         ctx.lineTo(pageX, pageY)
    
         // draw
         ctx.stroke()
     }
    


    9. End drawing
    After adding the monitoring of mouse movement/touch movement, we must remember to cancel the monitoring and end the drawing, otherwise it will always monitor and draw

    Here we create a closeDraw method as a callback method for mouse up/end touch to end drawing and remove the monitoring of mouse movement/touch movement. When the canvas finishes drawing, you need to call closePath() to let it finish drawing

    const closeDraw = () => {
         // end drawing
         ctx. closePath()
         // Remove mouse movement or gesture movement listeners
         window. removeEventListener("mousemove", draw)
     }
    

    Add end callback listener

    // create mouse/gesture popup/leave listener
     window.addEventListener(mobileStatus ? "touchend" : "mouseup", closeDraw)
    


    Ok, now our electronic signature function is almost finished, and now we can sign normally

    10. Cancel function/Clear canvas

    const cancel = () => {
         // Clear all drawing content on the current canvas
         ctx. clearRect(0, 0, config. width, config. height)
     }
    

    Bind this method to the cancel button

       <button onclick="cancel()">cancel</button>
    


    11. Save function
    There are many ways to save the content on the canvas as a picture/file, the more common ones are blob and toDataURL, but toDataURL is not as good as blob, so here we use the a tag + blob solution to save and download pictures

    // save - save the canvas content as an image
     const save = () => {
         // Convert the content on the canvas to a blob stream
         canvas.toBlob(blob => {
             // Get the current time and convert it into a string, which is used as a file name
             const date = Date.now().toString()
             // Create an a tag
             const a = document. createElement('a')
             // Set the download file name of the a tag
             a.download = `${date}.png`
             // Set the jump path of the a label to the file stream address
             a.href = URL.createObjectURL(blob)
             // Manually trigger the click event of the a tag
             a. click()
             // remove the a tag
             a. remove()
         })
     }
    <button onclick="save()">save</button>
    

    We will save the content we just drew, click the save button, and it will be downloaded and saved


    Full code

    <!DOCTYPE html>
    <html lang="en">
    <head>
         <meta charset="UTF-8">
         <meta http-equiv="X-UA-Compatible" content="IE=edge">
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <title>Document</title>
         <style>
             * {
                 margin: 0;
                 padding: 0;
             }
         </style>
    </head>
    <body>
         <canvas></canvas>
         <div>
             <button onclick="cancel()">Cancel</button>
             <button onclick="save()">Save</button>
         </div>
    </body>
    <script>
         // configuration content
         const config = {
             width: 400, // width
             height: 200, // height
             lineWidth: 5, // line width
             strokeStyle: 'red', // line color
             lineCap: 'round', // set the rounded corners at both ends of the line
             lineJoin: 'round', // rounded corners where lines meet
         }
    
         // get the canvas instance
         const canvas = document. querySelector('canvas')
         // set width and height
         canvas.width = config.width
         canvas.height = config.height
         // set a border
         canvas.style.border = '1px solid #000'
         // create context
         const ctx = canvas. getContext('2d')
    
         // set fill background color
         ctx.fillStyle = 'transparent'
         // draw filled rectangle
         ctx.fillRect(
             0, // x-axis start drawing position
             0, // y-axis starting drawing position
             config.width, // width
             config.height // height
         );
    
         // save the last drawn coordinates and offset
         const client = {
             offsetX: 0, // offset
             offsetY: 0,
             endX: 0, // coordinates
             endY: 0
         }
    
         // Determine whether it is a mobile terminal
         const mobileStatus = (/Mobile|Android|iPhone/i.test(navigator.userAgent))
    
         // initialization
         const init = event => {
             // Get the offset and coordinates
             const { offsetX, offsetY, pageX, pageY } = mobileStatus ? event.changedTouches[0] : event
    
             // Modify the last offset and coordinates
             client.offsetX = offsetX
             client.offsetY = offsetY
             client.endX = pageX
             client.endY = pageY
    
             // Clear all paths after the last beginPath and draw
             ctx.beginPath()
             // Set the corresponding configuration according to the configuration file
             ctx.lineWidth = config.lineWidth
             ctx.strokeStyle = config.strokeStyle
             ctx.lineCap = config.lineCap
             ctx.lineJoin = config.lineJoin
             // Set the starting point of the line
             ctx.moveTo(client.endX, client.endY)
             // Listen for mouse movement or gesture movement
             window. addEventListener(mobileStatus ? "touchmove" : "mousemove", draw)
         }
         // draw
         const draw = event => {
             // Get the current coordinate point
             const { pageX, pageY } = mobileStatus ? event. changedTouches[0] : event
             // Modify the coordinate point of the last drawing
             client.endX = pageX
             client.endY = pageY
    
             // Add lines according to coordinate point movement
             ctx.lineTo(pageX, pageY)
    
             // draw
             ctx.stroke()
         }
         // end drawing
         const closeDraw = () => {
             // end drawing
             ctx. closePath()
             // Remove mouse movement or gesture movement listeners
             window. removeEventListener("mousemove", draw)
         }
         // create mouse/gesture down listener
         window. addEventListener(mobileStatus ? "touchstart" : "mousedown", init)
         // create mouse/gesture popup/leave listener
         window.addEventListener(mobileStatus ? "touchend" : "mouseup", closeDraw)
    
         // cancel - clear the canvas
         const cancel = () => {
             // Clear all drawing content on the current canvas
             ctx. clearRect(0, 0, config. width, config. height)
         }
         // save - save the canvas content as an image
         const save = () => {
             // Convert the content on the canvas to a blob stream
             canvas.toBlob(blob => {
                 // Get the current time and convert it into a string, which is used as a file name
                 const date = Date.now().toString()
                 // Create an a tag
                 const a = document. createElement('a')
                 // Set the download file name of the a tag
                 a.download = `${date}.png`
                 // Set the jump path of the a label to the file stream address
                 a.href = URL.createObjectURL(blob)
                 // Manually trigger the click event of the a tag
                 a. click()
                 // remove the a tag
                 a. remove()
             })
         }
    </script>
    </html>
    

    Prompt in the applet:
    In the applet, we need to modify the Api that creates the instance and context, because there is no dom in the applet

    • If it is uni-app, you need to use uni.createCanvasContext for context creation

    • If it is a native WeChat applet, use wx.createCanvasContext to create (2.9.0) and later libraries do not support

    Popular posts from this blog

    大学资料分享——广工计院各个科目实验报告、课设及期末复习资料!!

    JAVA Traffic Signal Light Course Design (Source Code + Report + Video Demonstration)

    Win10 远程计算机或设备将不接受连接