使用 HTML5 Canvas 实现签名功能

在现代网页应用中,电子签名越来越常见,尤其是在合同签署和表单提交中。使用 HTML5 的 <canvas> 元素,我们可以轻松地创建一个签名捕捉工具。本文将带你一步步实现一个简单的签名应用程序。

1. 项目准备

创建基本 HTML 结构:首先,我们需要设置 HTML 页面。我们将包含一个画布元素用于绘制签名,以及一个按钮用于保存签名。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .wrap {
            margin: 50px;
            width: 500px;
        }

        canvas {
            border: 1px solid #000;
        }

        .save-btn {
            width: 200px;
            height: 30px;
            line-height: 30px;
            font-size: 16px;
            color: #999;
            cursor: pointer;
            border: 1px solid;
            text-align: center;
            margin: 0 auto;
        }
    </style>
</head>

<body>
    <div class="wrap">
        <canvas id="canvas"></canvas>
        <div class="save-btn">保存</div>
    </div>
</body>
</html>

2. JavaScript 实现签名功能

接下来,我们需要添加 JavaScript 代码,以便处理用户的绘图操作并保存签名。

初始化 Canvas

首先,我们获取画布元素并设置其大小和绘图上下文:

const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
canvas.width = 500;  // 设定画布宽度
canvas.height = 300; // 设定画布高度
let isDrawing = false;

处理绘图事件

我们将添加事件监听器,以便用户能够使用鼠标或触摸在画布上绘制签名。这个功能主要用到三个事件,鼠标按下,鼠标移动,鼠标离开。鼠标按下允许移动调用beginPath方法。在鼠标移动事件中,先用getBoundingClientRect对象获取canvas在视图中的位置,计算相对坐标。 lineTo方法设置绘制坐标,再调用stroke方法进行绘制。

按下开启一个路径

const canvas = document.querySelector('#canvas');
const ctx = canvas.getContext('2d');
const saveBtn = document.querySelector('.save-btn');
canvas.width = 500;
canvas.height = 300;
let isDrawing = false;
canvas.addEventListener('mousedown', (e) => {
    isDrawing = true;
    ctx.beginPath();
})

canvas.addEventListener('mousemove', (e) => {
    if (isDrawing) {
        const rect = canvas.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        ctx.lineWidth = 2;
        ctx.lineCap = 'round';
        ctx.strokeStyle = 'red';
        ctx.lineTo(x, y);
        ctx.stroke()
    }
})

canvas.addEventListener('mouseup', () => {
    isDrawing = false;
})

添加移动端支持

canvas.addEventListener('touchstart', (e) => {
    isDrawing = true;
    ctx.beginPath();
    e.preventDefault(); // 防止触摸移动时页面滚动
});

canvas.addEventListener('touchmove', (e) => {
    if (isDrawing) {
        const rect = canvas.getBoundingClientRect();
        const x = e.touches[0].clientX - rect.left;
        const y = e.touches[0].clientY - rect.top;
        ctx.lineWidth = 2;
        ctx.lineCap = 'round';
        ctx.strokeStyle = 'red';
        ctx.lineTo(x, y);
        ctx.stroke();
    }
});

canvas.addEventListener('touchend', () => {
    isDrawing = false;
});

保存签名功能

最后,我们需要实现保存签名的功能。我们将使用 toBlob 方法将画布内容转换为图片。

const saveBtn = document.querySelector('#saveBtn');
saveBtn.addEventListener('click', () => {
    saveSignature();
});

function saveSignature() {
    canvas.toBlob((blob) => {
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = 'signature.png'; // 指定下载文件的名称
        a.click();
    }, 'image/png');
}

3. 完整代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .wrap {
            margin: 50px;
            width: 500px;
        }

        canvas {
            border: 1px solid #000;
        }

        .save-btn {
            width: 200px;
            height: 30px;
            line-height: 30px;
            font-size: 16px;
            color: #999;
            cursor: pointer;
            border: 1px solid;
            text-align: center;
            margin: 0 auto;
        }
    </style>
</head>

<body>
    <div class="wrap">
        <canvas id="canvas"></canvas>
        <div class="save-btn">保存</div>
    </div>

    <script>
        const canvas = document.querySelector('#canvas');
        const ctx = canvas.getContext('2d');
        const saveBtn = document.querySelector('.save-btn');
        canvas.width = 500;
        canvas.height = 300;
        let isDrawing = false;
        // 开始绘图
        canvas.addEventListener('mousedown', (e) => {
            isDrawing = true;
            ctx.beginPath();
        })
        // 绘制
        canvas.addEventListener('mousemove', (e) => {
            if (isDrawing) {
                const rect = canvas.getBoundingClientRect();
                const x = e.clientX - rect.left;
                const y = e.clientY - rect.top;
                ctx.lineWidth = 2;
                ctx.lineCap = 'round';
                ctx.strokeStyle = 'red';
                ctx.lineTo(x, y);
                ctx.stroke()
            }
        })

        // 停止绘图
        canvas.addEventListener('mouseup', () => {
            isDrawing = false;
        })

        // 开始绘图
        canvas.addEventListener('touchstart', (e) => {
            isDrawing = true;
            ctx.beginPath();
        })
        // 绘制
        canvas.addEventListener('touchmove', (e) => {
            if (isDrawing) {
                const rect = canvas.getBoundingClientRect();
                const x = e.touches[0].clientX - rect.left;
                const y = e.touches[0].clientY - rect.top;
                ctx.lineWidth = 2;
                ctx.lineCap = 'round';
                ctx.strokeStyle = 'red';
                ctx.lineTo(x, y);
                ctx.stroke()
            }
        })

        // 停止绘图
        canvas.addEventListener('touchend', () => {
            isDrawing = false;
        })

        saveBtn.addEventListener('click', () => {
            save()
        })

        function save() {
            // 将画布内容转换为图片
            canvas.toBlob(function (blob) {
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                a.download = 'canvas_image.png'; // 指定下载文件的名称
                a.click()
            }, 'image/png');
        }
    </script>
</body>

</html>

 

posted @ 2024-11-01 01:22  雪旭  阅读(29)  评论(0编辑  收藏  举报