一文解读前端实现电子签名
bigegpt 2025-01-12 11:36 7 浏览
大家好,我是Echa。
创作不易,喜欢的老铁们加个关注,点个赞,后面会持续更新干货,速速收藏,谢谢!
在现在的时代发展中,从以前的手写签名,逐渐衍生出了电子签名。电子签名和纸质手写签名一样具有法律效应。电子签名目前主要还是在需要个人确认的产品环节和司法类相关的产品上较多。
举个常用的例子,大家都用过钉钉,钉钉上面就有电子签名,相信大家这肯定是知道的。
那作为前端的我们如何实现电子签名呢?其实在html5中已经出现了一个重要级别的辅助标签,是啥呢?那就是canvas。下面我给大家分享分享几个关于前端如何实现电子签名经典案例以及实现方法。
什么是canvas
Canvas(画布)是在HTML5中新增的标签用于在网页实时生成图像,并且可以操作图像内容,基本上它是一个可以用JavaScript操作的位图(bitmap)。Canvas 对象表示一个 HTML 画布元素 -。它没有自己的行为,但是定义了一个 API 支持脚本化客户端绘图操作。
大白话就是canvas是一个可以在上面通过javaScript画图的标签,通过其提供的context(上下文)及Api进行绘制,在这个过程中canvas充当画布的角色。
实现电子签名
知道几何的朋友都很清楚,线由点绘成,面由线绘成。
多点成线,多线成面。
所以我们实际只需要拿到当前触摸的坐标点,进行成线处理就可以了。
全文大纲
- vue-sign-canvas 一个基于canvas开发,封装于Vue组件的通用手写签名板
- Signature Pad 是一个用于绘制平滑签名的JavaScript库。
- 纯JavaScript实现电子签名,同时支持Web端和移动端。
vue-sign-canvas
在线预览:https://langyuxiansheng.github.io/vue-sign-canvas/
Github:https://github.com/langyuxiansheng/vue-sign-canvas
vue-sign-canvas 一个基于canvas开发,封装于Vue组件的通用手写签名板(电子签名板),支持pc端和移动端,属性支持自定义配置
组件模板使用
<template>
<div id="app">
<h2 class="title">Vue Sign Canvas 电子签名板</h2>
<sign-canvas class="sign-canvas" ref="SignCanvas" :options="options" v-model="value" />
<img v-if="value" class="view-image" :src="value" width="150" height="150" />
<div class="config">
<ul class="ul-config">
<li class="li-c">
<span class="item-label">书写速度:</span>
<span class="item-content">
<select name="isSign" v-model="options.isSign">
<option :value="true">签名</option>
<option :value="false">写字</option>
</select>
</span>
</li>
<li class="li-c">
<span class="item-label">显示边框/网格:</span>
<span class="item-content">
<select name="isSign" v-model="options.isShowBorder">
<option :value="true">显示</option>
<option :value="false">不显示</option>
</select>
</span>
</li>
<li class="li-c">
<span class="item-label">兼容高倍屏高清绘制:</span>
<span class="item-content">
<select name="isSign" v-model="options.isDpr">
<option :value="true">启用</option>
<option :value="false">关闭</option>
</select>
</span>
</li>
<li class="li-c">
<span class="item-label">边框宽度:</span>
<span class="item-content">
<input v-model="options.borderWidth" type="number" />
</span>
</li>
<li class="li-c">
<span class="item-label">下笔宽度:</span>
<span class="item-content">
<input v-model="options.writeWidth" type="number" />
</span>
</li>
<li class="li-c">
<span class="item-label">图片类型:</span>
<span class="item-content">
<input v-model="options.imgType" type="text" />
</span>
</li>
<li class="li-c">
<span class="item-label">线条的边缘类型:</span>
<span class="item-content">
<select name="lineCap" v-model="options.lineCap">
<option value="butt">平直的边缘</option>
<option value="round">圆形线帽</option>
<option value="square">正方形线帽</option>
</select>
</span>
</li>
<li class="li-c">
<span class="item-label">线条交汇时边角的类型:</span>
<span class="item-content">
<select name="lineCap" v-model="options.lineJoin">
<option value="bevel">创建斜角</option>
<option value="round">创建圆角</option>
<option value="miter">创建尖角</option>
</select>
</span>
</li>
<li class="li-c">
<span class="item-label">画笔颜色:</span>
<span class="item-content">
<input type="color" v-model="options.writeColor" />
</span>
</li>
<li class="li-c">
<span class="item-label">背景色:</span>
<span class="item-content">
<input type="color" v-model="options.bgColor" />
</span>
</li>
</ul>
</div>
<div class="sign-btns">
<span id="clear" @click="canvasClear()">清空</span>
<span id="save" @click="saveAsImg()">保存</span>
<span id="save" @click="downloadSignImg()">下载</span>
</div>
</div>
</template>
<script>
import SignCanvas from "../packages";
export default {
components: { SignCanvas },
data() {
return {
value: null,
options: {
isDpr: false, //是否使用dpr兼容高倍屏 [Boolean] 可选
lastWriteSpeed: 1, //书写速度 [Number] 可选
lastWriteWidth: 2, //下笔的宽度 [Number] 可选
lineCap: "round", //线条的边缘类型 [butt]平直的边缘 [round]圆形线帽 [square] 正方形线帽
lineJoin: "bevel", //线条交汇时边角的类型 [bevel]创建斜角 [round]创建圆角 [miter]创建尖角。
canvasWidth: 350, //canvas宽高 [Number] 可选
canvasHeight: 370, //高度 [Number] 可选
isShowBorder: true, //是否显示边框 [可选]
bgColor: "#fcc", //背景色 [String] 可选
borderWidth: 1, // 网格线宽度 [Number] 可选
borderColor: "#ff787f", //网格颜色 [String] 可选
writeWidth: 5, //基础轨迹宽度 [Number] 可选
maxWriteWidth: 30, // 写字模式最大线宽 [Number] 可选
minWriteWidth: 5, // 写字模式最小线宽 [Number] 可选
writeColor: "#101010", // 轨迹颜色 [String] 可选
isSign: true, //签名模式 [Boolean] 默认为非签名模式,有线框, 当设置为true的时候没有任何线框
imgType: "png", //下载的图片格式 [String] 可选为 jpeg canvas本是透明背景的
},
};
},
methods: {
/**
* 清除画板
*/
canvasClear() {
this.$refs.SignCanvas.canvasClear();
},
/**
* 保存图片
*/
saveAsImg() {
const img = this.$refs.SignCanvas.saveAsImg();
alert(`image 的base64:${img}`);
},
/**
* 下载图片
*/
downloadSignImg() {
this.$refs.SignCanvas.downloadSignImg();
},
},
};
</script>
<style lang="less">
* {
margin: 0;
padding: 0;
}
.title {
padding: 20px;
text-align: center;
}
.sign-canvas {
display: block;
margin: 20px auto;
}
.view-image {
display: block;
margin: 20px auto;
}
.config {
width: 350px;
margin: 20px auto;
.ul-config {
.li-c {
display: flex;
align-items: center;
padding: 4px 10px;
.item-label {
font-size: 14px;
}
.item-content {
margin-left: 10px;
}
}
}
}
.sign-btns {
display: flex;
justify-content: space-between;
#clear,
#clear1,
#save {
display: inline-block;
padding: 5px 10px;
width: 76px;
height: 40px;
line-height: 40px;
border: 1px solid #eee;
background: #e1e1e1;
border-radius: 10px;
text-align: center;
margin: 20px auto;
cursor: pointer;
}
}
</style>
横屏全屏模式下签名要怎么显示?
<div class="user-sign">
<template v-if="sign">
<img class="sign-image" :src="sign" alt="" srcset="" />
</template>
</div>
<script>
//局部注册
import SignCanvas from "sign-canvas";
import util from "@util";
import { saveSignature } from "@/http";
export default {
name: "UserSign",
components: { SignCanvas },
data() {
return {
sign: null,
};
},
};
</script>
<style lang="scss" scoped>
.user-sign {
background: #e7e7e7;
height: 9.375rem;
position: relative;
.sign-image {
margin: 0 auto;
z-index: 9;
height: 100%;
transform: rotate(-90deg) scale(1.5);
display: block;
}
}
</style>
如下图:
Signature Pad
在线预览:http://szimek.github.io/signature_pad/
Github:https://github.com/szimek/signature_pad
Signature Pad是一个用于绘制平滑签名的JavaScript库。它基于HTML5画布,使用基于Square发布的Smoother Signatures的可变宽度Bézier曲线插值。它适用于所有现代桌面和移动浏览器,不依赖任何外部库。
核心代码:
const canvas = document.querySelector("canvas");
const signaturePad = new SignaturePad(canvas);
// Returns signature image as data URL (see https://mdn.io/todataurl for the list of possible parameters)
signaturePad.toDataURL(); // save image as PNG
signaturePad.toDataURL("image/jpeg"); // save image as JPEG
signaturePad.toDataURL("image/jpeg", 0.5); // save image as JPEG with 0.5 image quality
signaturePad.toDataURL("image/svg+xml"); // save image as SVG data url
// Return svg string without converting to base64
signaturePad.toSVG(); // "<svg...</svg>"
signaturePad.toSVG({includeBackgroundColor: true}); // add background color to svg output
// Draws signature image from data URL (mostly uses https://mdn.io/drawImage under-the-hood)
// NOTE: This method does not populate internal data structure that represents drawn signature. Thus, after using #fromDataURL, #toData won't work properly.
signaturePad.fromDataURL("data:image/png;base64,iVBORw0K...");
// Draws signature image from data URL and alters it with the given options
signaturePad.fromDataURL("data:image/png;base64,iVBORw0K...", { ratio: 1, width: 400, height: 200, xOffset: 100, yOffset: 50 });
// Returns signature image as an array of point groups
const data = signaturePad.toData();
// Draws signature image from an array of point groups
signaturePad.fromData(data);
// Draws signature image from an array of point groups, without clearing your existing image (clear defaults to true if not provided)
signaturePad.fromData(data, { clear: false });
// Clears the canvas
signaturePad.clear();
// Returns true if canvas is empty, otherwise returns false
signaturePad.isEmpty();
// Unbinds all event handlers
signaturePad.off();
// Rebinds all event handlers
signaturePad.on();
如下图:
纯JavaScript实现电子签名
完整版:
<!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>
各内核和浏览器支持情况
Mozilla 程序从 Gecko 1.8 (Firefox 1.5 (en-US)) 开始支持 <canvas>。它首先是由 Apple 引入的,用于 OS X Dashboard 和 Safari。Internet Explorer 从 IE9 开始支持<canvas> ,更旧版本的 IE 中,页面可以通过引入 Google 的 Explorer Canvas 项目中的脚本来获得<canvas>支持。Google Chrome 和 Opera 9+ 也支持 <canvas>。
小程序中提示
在小程序中我们如果需呀实现的话,也是同样的原理哦,只是我们需要将创建实例和上下文的Api进行修改,因为小程序中是没有dom,既然没有dom,哪来的操作dom这个操作呢。
- 如果是uni-app则需要使用uni.createCanvasContext进行上下文创建
- 如果是原生微信小程序则使用wx.createCanvasContext进行创建(2.9.0)之后的库不支持
- 上一篇:免费领京豆 第五课 青龙面板的设置
- 下一篇:网页文档的复制与下载
相关推荐
- 10w qps缓存数据库——Redis(redis缓存调优)
-
一、Redis数据库介绍:Redis:非关系型缓存数据库nosql:非关系型数据库没有表,没有表与表之间的关系,更不存在外键存储数据的形式为key:values的形式c语言写的服务(监听端口),用来存...
- Redis系列专题4--Redis配置参数详解
-
本文基于windowsX64,3.2.100版本讲解,不同版本默认配置参数不同在Redis中,Redis的根目录中有一个配置文件(redis.conf,windows下为redis.windows....
- 开源一夏 | 23 张图,4500 字从入门到精通解释 Redis
-
redis是目前出场率最高的NoSQL数据库,同时也是一个开源的数据结构存储系统,在缓存、数据库、消息处理等场景使用的非常多,本文瑞哥就带着大家用一篇文章入门这个强大的开源数据库——Redis。...
- redis的简单与集群搭建(redis建立集群)
-
Redis是什么?是开源免费用c语言编写的单线程高性能的(key-value形式)内存数据库,基于内存运行并支持持久化的nosql数据库作用主要用来做缓存,单不仅仅是做缓存,比如:redis的计数器生...
- 推荐几个好用Redis图形化客户端工具
-
RedisPlushttps://gitee.com/MaxBill/RedisPlusRedisPlus是为Redis可视化管理开发的一款开源免费的桌面客户端软件,支持Windows、Linux...
- 关于Redis在windows上运行及fork函数问题
-
Redis在将数据库进行持久化操作时,需要fork一个进程,但是windows并不支持fork,导致在持久化操作期间,Redis必须阻塞所有的客户端直至持久化操作完成。微软的一些工程师花费时间在解决在...
- 你必须懂的Redis十大应用场景(redis常见应用场景)
-
Redis作为一款高性能的键值存储数据库,在互联网业务中有着广泛的应用。今天,我们就来详细盘点一下Redis的十大常用业务场景,并附上Golang的示例代码和简图,帮助大家更好地理解和应用Redis。...
- 极简Redis配置(redis的配置)
-
一、概述Redis的配置文件位于Redis安装目录下,文件名为redis.conf(Windows名为redis.windows.conf,linux下的是redis.conf)你可以通过C...
- 什么是redis,怎么启动及如何压测
-
从今天起咱们一起来学习一下关于“redis监控与调优”的内容。一、Redis介绍Redis是一种高级key-value数据库。它跟memcached类似,不过数据可以持久化,而且支持的数据类型很丰富。...
- 一款全新Redis UI可视化管理工具,支持WebUI和桌面——P3X Redis UI
-
介绍P3XRedisUI这是一个非常实用的RedisGUI,提供响应式WebUI访问或作为桌面应用程序使用,桌面端是跨平台的,而且完美支持中文界面。Githubhttps://github....
- windows系统的服务器快速部署java项目环境地址
-
1、mysql:https://dev.mysql.com/downloads/mysql/(msi安装包)2、redis:https://github.com/tporadowski/redis/r...
- window11 下 redis 下载与安装(windows安装redis客户端)
-
#热爱编程是一种怎样的体验#window11下redis下载与安装1)各个版本redis下载(windows)https://github.com/MicrosoftArchive/r...
- 一款轻量级的Redis客户端工具,贼好用!
-
使用命令行来操作Redis是一件非常麻烦的事情,我们一般会选用客户端工具来操作Redis。今天给大家分享一款好用的Redis客户端工具TinyRDM,它的界面清新又优雅,希望对大家有所帮助!简介Ti...
- 一个.NET开发且功能强大的Windows远程控制系统
-
我们致力于探索、分享和推荐最新的实用技术栈、开源项目、框架和实用工具。每天都有新鲜的开源资讯等待你的发现!项目介绍SiMayRemoteMonitorOS是一个基于Windows的远程控制系统,完...
- Redis客户端工具详解(4款主流工具)
-
大家好,我是mikechen。Redis是大型架构的基石,也是大厂最爱考察内容,今天就给大家重点详解4款Redis工具@mikechen本篇已收于mikechen原创超30万字《阿里架构师进阶专题合集...
- 一周热门
- 最近发表
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- resize函数 (64)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- mybatis大于等于 (64)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- skip-name-resolve (63)
- linuxlink (65)
- pythonwget (67)
- logstashinput (65)
- hadoop端口 (65)
- vue阻止冒泡 (67)
- oracle时间戳转换日期 (64)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)