隨著電腦和網路技術的快速發展,線上簽名技術越來越多的被應用在無紙化辦公中,這種直觀便利的操作不僅可以大幅提升辦公效率,而且使用數位化儲存方式,避開了傳統的紙本簽名存放查閱困難等問題。在我們日常生活中,已經有許多場景使用線上簽名技術,例如:pos機刷卡簽字、快遞簽收簽字、銀行或機關單位業務辦理簽字等。最近在做公司的業務辦理需求,裡面也涉及到線上簽名,我們採用的Canvas 技術實現,接下來,讓我們來聊聊如何使用Canvas 實現線上簽名吧!
什麼是Canvas?Canvas 是HTML5 新增的元素,用於在網頁上繪製圖形,它由Apple 在Safari 1.3 Web 瀏覽器中引入,之所以對HTML 擴展的原因在於, HTML 在Safari 中的繪圖能力能為Mac OS X 桌面的Dashboard 元件所使用,而Apple 也希望有一種方式可以在Dashboard 中支援腳本化的圖形。 Firefox 1.5 和Opera 9 這兩個瀏覽器也緊跟著Safari 的引領,開始支援Canvas 。
現在,Canvas 標籤已經是HTML5 最偉大的改進之一,因為它可以讓我們在不使用圖片的情況下實現網頁的圖形設計。它就像一塊畫布,本身沒有繪製能力,但卻把繪製API 展現給客戶端JavaScript,我們藉助JavaScript 的支持,在畫布範圍內盡情發揮,達到想要的效果。
技術選型這個功能無論是Canvas、SVG 或是Flash,都可以實現,但是我們為什麼選擇了Canvas 呢?
首先,由於功能上我們需要支援行動平台,所以Flash 我們就可以直接棄掉,它在行動端方面並沒有得到友好的支持,但Canvas 和SVG 都具有很好的跨平台能力,我們如何抉擇,下面我們來對比一下。
兩者各有自己的擅長領域, 基於以上,我們選擇了Canvas 來實現簽字功能。
下面,我們來看看實現效果。
了解了Canvas 來源、技術選型和最終呈現效果,接下來、我們會從創作、繪製、監聽、重繪、圖片處理等五部分進行撰寫,讓我們一起走進Canvas 繪製的世界。
創建畫布首先,我們要判斷瀏覽器是否支援Canvas :
isCanvasSupported = (): boolean => { let elem = document.createElement('canvas'); return !!(elem.getContext && elem.getContext('2d'));}
然後根據判斷結果選擇建立Canvas 畫布還是展示提示
{isCanvasSupported ? <canvas ref={canvas => (this.canvas = canvas)} height={canvasHeight} width={canvasWidth}> :對不起,目前瀏覽器暫時支援此功能! }
我們知道,每個Canvas 節點都有一個對應的context 物件, 我們可以透過Canvas 物件的getContext() 方法,直接把量字串2d 作為唯一的參數傳遞給它來取得。接下來,我們透過ref 取得Canvas 元素,再透過getContext() 方法得到一個畫布上繪圖的環境。
let cxt = this.canvas.getContext('2d');this.setState({cxt: cxt});
環境已經準備妥當,接下來我們就開始進行繪圖工作吧!
繪製首先繪製開始路徑:
cxt.beginPath();
然後設定目前線條的寬度:
cxt.lineWidth = 5;
設定線條的顏色:
cxt.strokeStyle = '#000';
透過moveTo 和lineTo ,我們來繪製一條線
cxt.moveTo(0,0);cxt.lineTo(150,0);// 繪製已定義的路徑cxt.stroke()
但是,我們發現繪製的線條比較生硬
這時,我們可以透過lineCap 改變線條末端線帽的樣式,為每個末端添加圓形線帽,減少線條的生硬感
cxt.lineCap = 'round';
同時,我們也可以透過設定lineJoin,指定條線交會時為圓形邊角
cxt.lineJoin = 'round';
但我們又發現,繪製的線條有明顯的鋸齒,此時我們就需要藉助Canvas 為我們提供的繪製元素陰影的功能來模糊邊緣出現的鋸齒,因為有陰影,所以我們可以適當地改變lineWidth 值
cxt.shadowBlur = 1;cxt.shadowColor = '#000';
是不是變得圓潤很多,到這裡,我們繪製線路的方法已經準備完事,接下來我們來看看怎麼監聽畫布事件來實現連貫執行繪製吧!
監聽畫布事件因為我們需要同時相容於PC 端和行動端,所以我們需要事先需要判斷對應執行的事件
this.state = {events: ('ontouchstart' in window) ? ['touchstart', 'touchmove', 'touchend'] : ['mousedown', 'mousemove', 'mouseup']}
在畫布初始化之後,我們開始監聽events[0] 事件
this.canvas.addEventListener(this.events[0], startEventHandler, false);
在startEventHandler函式中監聽events[1] 和events[2] 事件
this.canvas.addEventListener(events[1], moveEventHandler, false);this.canvas.addEventListener(events[2], endEventHandler, false);
重點來了,我們核心的內容就是計算、描繪劃過的路徑
moveEventHandler(event: any): void { event.preventDefault(); const {ctx, 是[0] : void { event.preventDefault(); const {ctx, isSupportTouch} = this.state; const evt = isSupportTouch ? event.touches[0] : event; const coverPos = this.canvasRectBoundingClient; ; const mouseX = evt.clientX - coverPos.left; const mouseY = evt.clientY - coverPos.top; cxt.lineTo( mouseX, mouseY ); cxt.stroke();}
了解Canvas 的知道, Canvas 畫布為我們提供了一個用來作圖的平面空間,該空間的每個點都有自己的座標,x 表示橫座標,y 表示垂直座標。原點(0, 0) 位於影像左上角,x 軸的正向是原點向右,y 軸的正向是原點向下。
於是我們透過getBoundingClientRect() 方法獲得頁面Canvas 元素相對瀏覽器視窗的位置左邊和頂部的像素距離,再利用clientX,clientY 事件屬性返回當事件被觸發時滑鼠指標向對於瀏覽器頁面的水平和垂直座標,最後透過lineTo 和stroke 來繪製路徑。
同時,我們要記得在events[2] 事件執行之後,移除events[1]、events[2] 事件,否則會造成一直繪製。
endEventHandler(event: any): void { event.preventDefault(); const {events, moveEventHandler, endEventHandler} = this.state; this.canvas.removeEventListener(events[1], moveListHandler, false); .canvas events[2], endEventHandler, false);}
如此反覆循環上述事件操作,我們的簽字功能就基本實現了。
重新繪製在簽字過程中,簽錯或是簽的過於潦草是必不可免的,所以我們需要支援清空簽字的功能,這時,我們利用Canvas 的clearRect() 方法就可以幫助我們清除畫布區域內容。
cxt.clearRect(0, 0, canvasWidth, canvasHeight);圖片處理
繪製之後我們還沒完事,我們還需要把繪製的簽名上傳儲存。這時,我們可以利用toDataURL() 方法將Canvas 轉換成一般的圖像檔案形式。
通常我們直接執行以操作就能轉換成data URI,然後再利用ajax 請求上傳就完事了。
dataurl = this.canvas.toDataURL('image/png');//ordataurl = this.canvas.toDataURL('image/jpeg', 0.8);
但是,由於各種業務需求,我們有時需要攜帶頁面其他內容,這時,我們可以藉助html2canvas 來實現。 html2canvas 可以幫助我們對瀏覽器端整個或部分頁面進行截屏,並渲染成一個Canvas ,然後我們在利用toDataURL() 方法進行處理。
說html2canvas,順便給大家一個繞坑提示,它在一些低版本瀏覽器截出來的圖片是空白的,原因是使用了flex 佈局,而html2canvas並不支援-webkit-flex 或-webkit-box,所以無法將HTML 產生Canvas,從而導致了截出一張白螢幕。
解決辦法:透過以上幾步,我們就基本上實現了線上簽名的功能。值得注意的是,這個專案我們使用的React+TypeScript 環境構建,上述程式碼的實際使用需要結合自己環境進行適當修改。
文中使用的是Canvas 比較淺層的繪製知識,如果想利用Canvas進動畫製作、物理效果模擬、碰撞檢測、遊戲開發、行動應用開發、大數據視覺化開發,還需要我們複習一下之前學過的數學幾何、物理方面的知識,然後在慢慢摸索。現在很多成熟的圖表插件都是用Canvas 實現的,例如Chart.js、ECharts等,裡面很多好看酷炫的圖表,幾乎涵蓋了所有圖表的實作。 Canvas還有很多開源類別庫,例如ZRender、createJS、Pixi.js等,ECharts底層也是依賴輕量級的Canvas 類別庫ZRender 進行封裝的。
好了,我們今天就先聊到這裡,如有任何疑問,請留言。以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。