這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 在現在的時代發展中,從以前的手寫簽名,逐漸衍生出了電子簽名。電子簽名和紙質手寫簽名一樣具有法律效應。電子簽名目前主要還是在需要個人確認的產品環節和司法類相關的產品上較多。 舉個常用的例子,大家都用過釘釘,釘釘上面就有電子簽名,相信大 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
前言
在現在的時代發展中,從以前的手寫簽名,逐漸衍生出了電子簽名。電子簽名和紙質手寫簽名一樣具有法律效應。電子簽名目前主要還是在需要個人確認的產品環節和司法類相關的產品上較多。
舉個常用的例子,大家都用過釘釘,釘釘上面就有電子簽名,相信大家這肯定是知道的。
那作為前端的我們如何實現電子簽名呢?其實在html5
中已經出現了一個重要級別的輔助標簽,是啥呢?那就是canvas。
什麼是canvas
Canvas(畫布)
是在HTML5
中新增的標簽用於在網頁實時生成圖像,並且可以操作圖像內容,基本上它是一個可以用JavaScript
操作的點陣圖(bitmap)
。Canvas
對象表示一個 HTML
畫布元素 -。它沒有自己的行為,但是定義了一個 API 支持腳本化客戶端繪圖操作。
大白話就是canvas
是一個可以在上面通過javaScript
畫圖的標簽,通過其提供的context(上下文)
及Api
進行繪製,在這個過程中canvas
充當畫布的角色。
<canvas></canvas>
如何使用
canvas
給我們提供了很多的Api
,供我們使用,我們只需要在body
標簽中創建一個canvas
標簽,在script
標簽中拿到canvas
這個標簽的節點,並創建context(上下文)
就可以使用了。
... <body> <canvas></canvas> </body> <script> // 獲取canvas 實例 const canvas = document.querySelector('canvas') canvas.getContext('2d') </script> ...
步入正題。
實現電子簽名
知道幾何的朋友都很清楚,線有點繪成,面由線繪成。
多點成線,多線成面。
所以我們實際只需要拿到當前觸摸的坐標點,進行成線處理就可以了。
在body
中添加canvas
標簽
在這裡我們不僅需要在在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
的方法作為保存畫布上的內容使用。
將畫布上的內容保存為圖片/文件
的方法有很多,比較常見的是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() }) }
然後我們將這個方法和保存按鈕
進行綁定
<button onclick="save()">保存</button>
我們將剛剛繪製的內容進行保存,點擊保存按鈕,就會進行下載保存
完整代碼
<!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)之後的庫不支持