Commencez le voyage de croissance des pépites ! C'est le 15ème jour de ma participation au "Nuggets Daily New Plan·December Update Challenge", cliquez pour voir les détails de l'événement
avant-propos
Dans le développement actuel de l'époque, des signatures manuscrites précédentes, les signatures électroniques sont progressivement dérivées. Les signatures électroniques ont le même effet juridique que les signatures manuscrites sur papier. À l'heure actuelle, les signatures électroniques sont principalement utilisées dans les liens de produits qui nécessitent une confirmation personnelle et les produits liés à la justice.
Pour donner un exemple courant, tout le monde a utilisé DingTalk, et il y a une signature électronique sur DingTalk, je pense que tout le monde doit le savoir.
Alors, comment implémentons-nous les signatures électroniques en tant que frontal ? En fait, html5
un niveau important de balises auxiliaires est apparu dans , qu'est-ce que c'est ? C'est de la toile .
qu'est-ce quecanvas
Canvas(画布)
Il s'agit HTML5
d'une nouvelle balise dans , qui est utilisée pour générer des images en temps réel sur des pages Web et qui peut manipuler le contenu des images. Fondamentalement, il s'agit d'une JavaScript
opération utilisable 位图(bitmap)
. Canvas
Objet représentant un HTML
CanvasElement - . Il n'a pas de comportement propre, mais définit une API pour prendre en charge les opérations de dessin côté client scriptées.
La langue vernaculaire est canvas
une étiquette qui peut être dessinée dessus , et tirée javaScript
à travers ce qu'elle fournit , et agit comme une toile dans le processus .context(上下文)
Api
canvas
<canvas></canvas>
复制代码
comment utiliser
canvas
Cela nous fournit beaucoup de choses Api
à utiliser. Nous avons seulement besoin de body
créer une canvas
étiquette dans l'étiquette, script
d'obtenir canvas
le nœud de cette étiquette dans l'étiquette et de context(上下文)
le créer pour l'utiliser.
...
<body>
<canvas></canvas>
</body>
<script>
// 获取canvas 实例
const canvas = document.querySelector('canvas')
canvas.getContext('2d')
</script>
...
复制代码
Passer aux choses sérieuses.
Mettre en place la signature électronique
Les amis qui connaissent la géométrie sont très clairs sur le fait que les lignes sont dessinées par des points et que les surfaces sont dessinées par des lignes.
Plusieurs points forment une ligne et plusieurs lignes forment un plan.
Nous avons donc seulement besoin d'obtenir le point de coordonnées du toucher actuel et d'effectuer un traitement de ligne.
body
Ajouter canvas
des balises dans
在这里我们不仅需要在在body
中添加canvas
标签,我们还需要添加两个按钮,分别是取消
和保存
(后面我们会用到)。
<body>
<canvas></canvas>
<div>
<button>取消</button>
<button>保存</button>
</div>
</body>
复制代码
添加文件
我这里全程使用js
进行样式设置及添加。
// 配置内容
const config = {
width: 400, // 宽度
height: 200, // 高度
lineWidth: 5, // 线宽
strokeStyle: 'red', // 线条颜色
lineCap: 'round', // 设置线条两端圆角
lineJoin: 'round', // 线条交汇处圆角
}
复制代码
获取canvas
实例
这里我们使用querySelector
获取canvas
的dom实例,并设置样式和创建上下文。
// 获取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')
复制代码
基础设置
我们将canvas
的填充色为透明,并绘制填充一个矩形,作为我们的画布,如果不设置这个填充背景色,在我们初识渲染的时候是一个黑色背景,这也是它的一个默认色。
// 设置填充背景色
ctx.fillStyle = 'transparent'
// 绘制填充矩形
ctx.fillRect(
0, // x 轴起始绘制位置
0, // y 轴起始绘制位置
config.width, // 宽度
config.height // 高度
);
复制代码
上次绘制路径保存
这里我们需要声明一个对象,用来记录我们上一次绘制的路径结束坐标点及偏移量。
- 保存上次坐标点这个我不用说大家都懂;
- 为啥需要保存偏移量呢,因为鼠标和画布上的距离是存在一定的偏移距离,在我们绘制的过程中需要减去这个偏移量,才是我们实际的绘制坐标。
- 但我发现
chrome
中不需要减去这个偏移量,拿到的就是实际的坐标,之前在微信小程序中使用就需要减去偏移量,需要在小程序中使用的朋友需要注意这一点哦。
// 保存上次绘制的 坐标及偏移量
const client = {
offsetX: 0, // 偏移量
offsetY: 0,
endX: 0, // 坐标
endY: 0
}
复制代码
设备兼容
我们需要它不仅可以在web
端使用,还需要在移动端
使用,我们需要给它做设备兼容处理。我们通过调用navigator.userAgent
获取当前设备信息,进行正则匹配判断。
// 判断是否为移动端
const mobileStatus = (/Mobile|Android|iPhone/i.test(navigator.userAgent))
复制代码
初始化
这里我们在监听鼠标按下(mousedown)
(web端)/触摸开始(touchstart)
的时候进行初始化,事件监听采用addEventListener
。
// 创建鼠标/手势按下监听器
window.addEventListener(mobileStatus ? "touchstart" : "mousedown", init)
复制代码
三元判断说明: 这里当
mobileStatus
为true
时则表示为移动端
,反之则为web端
,后续使用到的三元
依旧是这个意思。
声明初始化方法
我们添加一个init
方法作为监听鼠标按下
/触摸开始
的回调方法。
这里我们需要获取到当前鼠标按下
/触摸开始
的偏移量和坐标,进行起始点绘制。
Tips:
web端
可以直接通过event
中取到,而移动端则需要在event.changedTouches[0]
中取到。
这里我们在初始化后再监听鼠标的移动。
// 初始化
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
方法,作为监听鼠标移动
/触摸移动
的回调方法。
// 绘制
const draw = event => {
// 获取当前坐标点位
const { pageX, pageY } = mobileStatus ? event.changedTouches[0] : event
// 修改最后一次绘制的坐标点
client.endX = pageX
client.endY = pageY
// 根据坐标点位移动添加线条
ctx.lineTo(pageX , pageY )
// 绘制
ctx.stroke()
}
复制代码
结束绘制
添加了监听鼠标移动
/触摸移动
我们一定要记得取消监听并结束绘制,不然的话它会一直监听并绘制的。
这里我们创建一个cloaseDraw
方法作为鼠标弹起
/结束触摸
的回调方法来结束绘制并移除鼠标移动
/触摸移动
的监听。
canvas
结束绘制则需要调用closePath()
让其结束绘制
// 结束绘制
const cloaseDraw = () => {
// 结束绘制
ctx.closePath()
// 移除鼠标移动或手势移动监听器
window.removeEventListener("mousemove", draw)
}
复制代码
添加结束回调监听器
// 创建鼠标/手势 弹起/离开 监听器
window.addEventListener(mobileStatus ? "touchend" :"mouseup", cloaseDraw)
复制代码
ok,现在我们的电子签名功能还差一丢丢可以实现完了,现在已经可以正常的签名了。
我们来看一下效果:
取消功能/清空画布
我们在刚开始创建的那两个按钮开始排上用场了。
这里我们创建一个cancel
的方法作为取消并清空画布使用
// 取消-清空画布
const cancel = () => {
// 清空当前画布上的所有绘制内容
ctx.clearRect(0, 0, config.width, config.height)
}
复制代码
然后我们将这个方法和取消按钮
进行绑定
<button onclick="cancel()">取消</button>
复制代码
保存功能
这里我们创建一个save
的方法作为保存画布上的内容使用。
Il existe de nombreuses façons de sauvegarder le contenu sur la toile , et ces deux solutions 图片/文件
sont plus courantes , mais ce copain n'est pas fort, et l'adaptation n'est pas bonne. Nous utilisons donc ici le schéma label➕ pour enregistrer et télécharger des images.blob
toDataURL
toDataURL
blob
a
blob
// 保存-将画布内容保存为图片
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()
})
}
复制代码
Ensuite, nous 保存按钮
lions cette méthode avec
<button onclick="save()">保存</button>
复制代码
Nous allons enregistrer le contenu que nous venons de dessiner, cliquez sur le bouton Enregistrer, et il sera téléchargé et enregistré
code complet
<!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>
复制代码
Prise en charge du noyau et du navigateur
Les programmes Mozilla sont pris en charge à partir de Gecko 1.8 ( Firefox 1.5 (en-US)<canvas>
) . Il a été introduit pour la première fois par Apple pour OS X Dashboard et Safari. Internet Explorer est pris en charge depuis IE9 <canvas>
. Dans les anciennes versions d'IE, les pages peuvent être prises en charge en introduisant des scripts du projet Explorer Canvas<canvas>
de Google . Google Chrome et Opera 9+ sont également pris en charge <canvas>
.
Invite dans l'applet
Si on a besoin de l'implémenter dans le petit programme, c'est le même principe, mais il faut le 创建实例和上下文
modifier Api
, car il n'y a pas une telle opération dans le petit programme, dom
puisqu'il n'y a pas une telle opération dom
, d'où 操作dom
vient cette opération ?
-
Si oui
uni-app
, vous devez utiliser uni.createCanvasContext pour la création de contexte -
S'il s'agit d'une applet WeChat native,
wx.createCanvasContext
la bibliothèque après création (2.9.0) n'est pas prise en charge