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

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

foreword

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.

To give a common example, everyone has used DingTalk, and there is an electronic signature on DingTalk, I believe everyone must know this.

So how do we implement electronic signatures as the front end? In fact, html5an important level of auxiliary tags has appeared in , what is it? That is canvas[2].

what iscanvas

Canvas(画布)[3] is HTML5a new tag in , which is used to generate images in real time on web pages, and can manipulate image content, basically it is a user- JavaScriptoperable 位图(bitmap). CanvasObject representing a HTMLCanvasElement - . It has no behavior of its own, but defines an API to support scripted client-side drawing operations.

The vernacular is canvasa label that can be drawn on it , and drawn javaScriptthrough what it provides , and acts as a canvas in the process .context(上下文)Apicanvas

<canvas></canvas>

how to use

canvasIt provides us with a lot Apifor us to use. We only need to bodycreate a canvaslabel in the label, scriptget canvasthe node of this label in the label, and create context(上下文)it to use.

<body>
    <canvas></canvas>
</body>
<script>
    // 获取canvas 实例
    const canvas = document.querySelector('canvas')
    canvas.getContext('2d')
</script>

Get down to business.

Implement electronic signature

Friends who know geometry are very clear that lines are drawn by points, and surfaces are drawn by lines.

Multiple points form a line, and multiple lines form a plane.

So we actually only need to get the coordinate point of the current touch and perform line processing.

bodyAdd canvastags in

Here we not only need to bodyadd canvaslabels in , we also need to add two buttons, namely 取消and 保存(we will use them later).

<body>
    <canvas></canvas>
    <div>
        <button>取消</button>
        <button>保存</button>
    </div>
</body>
复制代码

add files

I use it here jsfor style setting and adding.

// 配置内容
    const config = {
    
    
        width: 400, // 宽度
        height: 200, // 高度
        lineWidth: 5, // 线宽
        strokeStyle: 'red', // 线条颜色
        lineCap: 'round', // 设置线条两端圆角
        lineJoin: 'round', // 线条交汇处圆角
    }

get canvasinstance

Here we use the querySelectorobtained canvasdom instance, and set the style and create the context.

    // 获取canvas 实例
    const canvas = document.querySelector('canvas')
    // 设置宽高
    canvas.width = config.width
    canvas.height = config.height
    // 设置一个边框,方便我们查看及使用
    canvas.style.border = '1px solid #000'
    // 创建上下文
    const ctx = canvas.getContext('2d')

Basic Settings

We set canvasthe fill color to be transparent, and draw a filled rectangle as our canvas. If we do not set this fill background color, it will be a black background when we first know about rendering, which is also its default color.

    // 设置填充背景色
    ctx.fillStyle = 'transparent'
    // 绘制填充矩形
    ctx.fillRect(
        0, // x 轴起始绘制位置
        0, // y 轴起始绘制位置
        config.width, // 宽度
        config.height // 高度
    );

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.

  • I don’t need to say that everyone understands saving the last coordinate point;
  • 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 chromethat there is no need to subtract this offset, and what I get is the actual coordinates. Before using it in the WeChat applet, I need to subtract the offset. Friends who need to use it in the applet need to pay attention to this.
    // 保存上次绘制的 坐标及偏移量
    const client = {
    
    
        offsetX: 0, // 偏移量
        offsetY: 0,
        endX: 0, // 坐标
        endY: 0
    }

device compatible

We need it not only to be used on webthe terminal, but also to 移动端be used, and we need to make it compatible with devices. We navigator.userAgentobtain the current device information by calling, and make regular matching judgments.

    // 判断是否为移动端
    const mobileStatus = (/Mobile|Android|iPhone/i.test(navigator.userAgent))

复制代码

initialization

Here we initialize when listening 鼠标按下(mousedown)(web side) / , event monitoring adopts .触摸开始(touchstart)addEventListener

    // 创建鼠标/手势按下监听器
    window.addEventListener(mobileStatus ? "touchstart" : "mousedown", init)

Explanation of the ternary judgment: Here, when mobileStatusit istrue , it is expressed as 移动端, and vice versa , it is still web端used in the subsequent use .三元

declare initialization method

We add a initmethod as a callback method for listener 鼠标按下/ 触摸开始.

Here we need to get the offset and coordinates of the current 鼠标按下/ to draw the starting point.触摸开始

Tips: It can be obtained web端directly through the Internet, while the mobile terminal needs to be obtained in the Internet.eventevent.changedTouches[0]

Here we monitor the mouse movement after initialization.

    // 初始化
    const init = event => {
    
    
        // 获取偏移量及坐标
        const {
    
     offsetX, offsetY, pageX, pageY } = mobileStatus ? event.changedTouches[0] : event 

        // 修改上次的偏移量及坐标
        client.offsetX = offsetX
        client.offsetY = offsetY
        client.endX = pageX
        client.endY = pageY

        // 清除以上一次 beginPath 之后的所有路径,进行绘制
        ctx.beginPath()

        // 根据配置文件设置进行相应配置
        ctx.lineWidth = config.lineWidth
        ctx.strokeStyle = config.strokeStyle
        ctx.lineCap = config.lineCap
        ctx.lineJoin = config.lineJoin

        // 设置画线起始点位
        ctx.moveTo(client.endX, client.endY)

        // 监听 鼠标移动或手势移动
        window.addEventListener(mobileStatus ? "touchmove" : "mousemove", draw)
    }

draw

Here we add the drawing drawmethod as the callback method of listening 鼠标移动/ 触摸移动.

    // 绘制
    const draw = event => {
    
    
        // 获取当前坐标点位
        const {
    
     pageX, pageY } = mobileStatus ? event.changedTouches[0] : event
        // 修改最后一次绘制的坐标点
        client.endX = pageX
        client.endY = pageY

        // 根据坐标点位移动添加线条
        ctx.lineTo(pageX , pageY )

        // 绘制
        ctx.stroke()
    }
复制代码

end drawing

Added monitoring 鼠标移动/ 触摸移动We must remember to cancel the monitoring and end the drawing, otherwise it will always monitor and draw.

Here we create a cloaseDrawmethod as the callback method of 鼠标弹起/ 结束触摸to end the drawing and remove the listener of 鼠标移动/ .触摸移动

canvasTo end the drawing, you need to call to closePath()let it end the drawing

    // 结束绘制
    const cloaseDraw = () => {
    
    
        // 结束绘制
        ctx.closePath()
        // 移除鼠标移动或手势移动监听器
        window.removeEventListener("mousemove", draw)
    }
复制代码

Add end callback listener

    // 创建鼠标/手势 弹起/离开 监听器
    window.addEventListener(mobileStatus ? "touchend" :"mouseup", cloaseDraw)
    

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

Let's take a look at the effect:

Cancel function/clear canvas

The two buttons we created at the beginning come in handy.

Here we create a cancelmethod to cancel and clear the canvas

    // 取消-清空画布
    const cancel = () => {
    
    
        // 清空当前画布上的所有绘制内容
        ctx.clearRect(0, 0, config.width, config.height)
    }

Then we 取消按钮bind this method with

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

save function

Here we create a savemethod to save the content on the canvas.

There are many ways to save the content on the canvas , and these two solutions 图片/文件are more common , but this buddy is not strong, and the adaptation is not good. So here we use the label➕ scheme to save and download pictures.blobtoDataURLtoDataURLblobablob

    // 保存-将画布内容保存为图片
    const save = () => {
    
    
        // 将canvas上的内容转成blob流
        canvas.toBlob(blob => {
    
    
            // 获取当前时间并转成字符串,用来当做文件名
            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()
        })
    }

Then we 保存按钮bind this method with

    <button onclick="save()">保存</button>

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

picture

image.png

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()">取消</button>
        <button onclick="save()">保存</button>
    </div>
</body>
<script>
    // 配置内容
    const config = {
      
      
        width: 400, // 宽度
        height: 200, // 高度
        lineWidth: 5, // 线宽
        strokeStyle: 'red', // 线条颜色
        lineCap: 'round', // 设置线条两端圆角
        lineJoin: 'round', // 线条交汇处圆角
    }

    // 获取canvas 实例
    const canvas = document.querySelector('canvas')
    // 设置宽高
    canvas.width = config.width
    canvas.height = config.height
    // 设置一个边框
    canvas.style.border = '1px solid #000'
    // 创建上下文
    const ctx = canvas.getContext('2d')

    // 设置填充背景色
    ctx.fillStyle = 'transparent'
    // 绘制填充矩形
    ctx.fillRect(
        0, // x 轴起始绘制位置
        0, // y 轴起始绘制位置
        config.width, // 宽度
        config.height // 高度
    );

    // 保存上次绘制的 坐标及偏移量
    const client = {
      
      
        offsetX: 0, // 偏移量
        offsetY: 0,
        endX: 0, // 坐标
        endY: 0
    }

    // 判断是否为移动端
    const mobileStatus = (/Mobile|Android|iPhone/i.test(navigator.userAgent))

    // 初始化
    const init = event => {
      
      
        // 获取偏移量及坐标
        const {
      
       offsetX, offsetY, pageX, pageY } = mobileStatus ? event.changedTouches[0] : event 

        // 修改上次的偏移量及坐标
        client.offsetX = offsetX
        client.offsetY = offsetY
        client.endX = pageX
        client.endY = pageY

        // 清除以上一次 beginPath 之后的所有路径,进行绘制
        ctx.beginPath()
        // 根据配置文件设置相应配置
        ctx.lineWidth = config.lineWidth
        ctx.strokeStyle = config.strokeStyle
        ctx.lineCap = config.lineCap
        ctx.lineJoin = config.lineJoin
        // 设置画线起始点位
        ctx.moveTo(client.endX, client.endY)
        // 监听 鼠标移动或手势移动
        window.addEventListener(mobileStatus ? "touchmove" : "mousemove", draw)
    }
    // 绘制
    const draw = event => {
      
      
        // 获取当前坐标点位
        const {
      
       pageX, pageY } = mobileStatus ? event.changedTouches[0] : event
        // 修改最后一次绘制的坐标点
        client.endX = pageX
        client.endY = pageY

        // 根据坐标点位移动添加线条
        ctx.lineTo(pageX , pageY )

        // 绘制
        ctx.stroke()
    }
    // 结束绘制
    const cloaseDraw = () => {
      
      
        // 结束绘制
        ctx.closePath()
        // 移除鼠标移动或手势移动监听器
        window.removeEventListener("mousemove", draw)
    }
    // 创建鼠标/手势按下监听器
    window.addEventListener(mobileStatus ? "touchstart" : "mousedown", init)
    // 创建鼠标/手势 弹起/离开 监听器
    window.addEventListener(mobileStatus ? "touchend" :"mouseup", cloaseDraw)
    
    // 取消-清空画布
    const cancel = () => {
      
      
        // 清空当前画布上的所有绘制内容
        ctx.clearRect(0, 0, config.width, config.height)
    }
    // 保存-将画布内容保存为图片
    const save = () => {
      
      
        // 将canvas上的内容转成blob流
        canvas.toBlob(blob => {
      
      
            // 获取当前时间并转成字符串,用来当做文件名
            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()
        })
    }
</script>
</html>

Kernel and browser support

Mozilla programs support it starting with Gecko 1.8 (Firefox 1.5 (en-US)[4]) <canvas>. It was first introduced by Apple for OS X Dashboard and Safari. Internet Explorer has been supported since IE9 <canvas>. In older versions of IE, pages can be supported by introducing scripts from Google's Explorer Canvas[5] project <canvas>. Google Chrome and Opera 9+ are also supported <canvas>.

Prompt in the applet

If we need to implement it in the small program, it is the same principle, but we need to 创建实例和上下文modify Apiit, because there is no such operation in the small program, domsince there is no such operation dom, where does 操作domthis operation come from?

  • If yes uni-app, you need to use uni.createCanvasContext[6] for context creation

Guess you like

Origin blog.csdn.net/itwangyang520/article/details/128342521