app canvas渲染后图片黑色_H5 基于 canvas 实现电子签名并生成PDF文档
(給前端大全加星標(biāo),提升前端技能)
轉(zhuǎn)自:coyota666
https://juejin.cn/post/6901273585428463624
前言
電子簽名通俗來說就是通過技術(shù)手段實(shí)現(xiàn)在電子文檔上加載電子形式的簽名,其作用類似于紙質(zhì)合同上的手寫簽名或加蓋的公章。雖然電子簽名多年來合法性一直遭到質(zhì)疑,但其在企業(yè)工作流審批、請(qǐng)柬、單據(jù)保全等場(chǎng)景應(yīng)用廣泛,最近的項(xiàng)目中就有這樣一個(gè)手寫簽名并生成PDF文件的需求。
實(shí)現(xiàn)思路
生成簽名
1. 在tsx中定義canvas畫布
?"350"?height="150"?/>注意:Canvas的寬高必須要使用內(nèi)聯(lián)樣式定義,這是因?yàn)镃anvas標(biāo)簽有自己的默認(rèn)寬高300px×150px。它內(nèi)聯(lián)樣式定義的width和height是繪畫區(qū)域(畫布)實(shí)際寬度和高度,繪制的圖形都是在這個(gè)上面。如果在style外鏈文件中定義其width和height,那么這個(gè)width和height是Canvas在瀏覽器中被渲染的高度和寬度。如果Canvas中沒有直接定義width和height沒或值不正確,就會(huì)被設(shè)置成默認(rèn)值{width:300px,height:150px}。所以,如果你在style中外鏈文件中設(shè)置了canvas {width: 200px; height: 200px;},卻沒有直接在canvas上定義畫布寬高,那么此時(shí)你輸出canvas.height 值依舊為150,canvas.width值依舊為300。也就是一塊150×300的畫布在200×200的區(qū)域渲染,因而圖片會(huì)出現(xiàn)拉伸、變形等現(xiàn)象。
2. 定義簽名函數(shù)
?const?writing?=?(????beginX:?number,
????beginY:?number,
????stopX:?number,
????stopY:?number,
????ctx:?any,
??)?=>?{
????ctx.beginPath();??//?開啟一條新路徑
????ctx.globalAlpha?=?1;??//?設(shè)置圖片的透明度
????ctx.lineWidth?=?3;??//?設(shè)置線寬
????ctx.strokeStyle?=?'red';??//?設(shè)置路徑顏色
????ctx.moveTo(beginX,?beginY);??//?從(beginX,?beginY)這個(gè)坐標(biāo)點(diǎn)開始畫圖
????ctx.lineTo(stopX,?stopY);??//?定義從(beginX,?beginY)到(stopX,?stopY)的線條(該方法不會(huì)創(chuàng)建線條)
????ctx.closePath();??//?創(chuàng)建該條路徑
??? ctx.stroke();??//?實(shí)際地繪制出通過 moveTo()?和 lineTo()?方法定義的路徑。默認(rèn)顏色是黑色。
??};
3. 注冊(cè)監(jiān)聽事件
????let?beginX:?number,?beginY:?number;????const?canvas:?HTMLCanvasElement?=?canvasDom.current;
????const?ctx?=?canvas.getContext('2d');
????ctx.fillStyle?=?'#fff';
????ctx.fillRect(0,?0,?canvas.width,?canvas.height);
????canvas.addEventListener('touchstart',?function(event:?any)?{
??????event.preventDefault();?//?阻止在canvas畫布上簽名的時(shí)候頁面跟著滾動(dòng)
??????beginX?=?event.touches[0].clientX?-?this.offsetLeft;?
??????beginY?=?event.touches[0].pageY?-?this.offsetTop;
????});
????canvas.addEventListener('touchmove',?(event:?any)?=>?{
??????event.preventDefault();?//?阻止在canvas畫布上簽名的時(shí)候頁面跟著滾動(dòng)
??????event?=?event.touches[0];let?stopX?=?event.clientX?-?canvas.offsetLeft;let?stopY?=?event.pageY?-?canvas.offsetTop;
??????writing(beginX,?beginY,?stopX,?stopY,?ctx);
??????beginX?=?stopX;?//?這一步很關(guān)鍵,需要不斷更新起點(diǎn),否則畫出來的是射線簇
??????beginY?=?stopY;
????});
注意:
clientX/clientY: 觸摸位置距離當(dāng)前body可視區(qū)域的x,y坐標(biāo);
pageX/pageY: 對(duì)于整個(gè)頁面來說,觸摸位置距離body左上角的x,y坐標(biāo),包括被scrollTop和scrollLeft的值;
screenX/screenY: 觸摸位置距離顯示器左邊和頂部的x,y距離。
所以,在獲取結(jié)束點(diǎn)坐標(biāo)的時(shí)候,如果當(dāng)前頁面沒有出現(xiàn)滾動(dòng)條,使用clientY和pageY計(jì)算差別不大,如果頁面比較長(zhǎng),出現(xiàn)了滾動(dòng)條,那么就必須要使用pageY來計(jì)算。clientX同理,但是移動(dòng)端通常橫向滾動(dòng)的場(chǎng)景不多,所以用clientX來計(jì)算即可。
其實(shí)這個(gè)原理和微積分很相似,線段本質(zhì)上就是由無窮多個(gè)小線段組成,宏觀一點(diǎn)來看可以把線段當(dāng)成一個(gè)個(gè)長(zhǎng)度很小的小線段首尾相連構(gòu)成。所以我一直覺得編程編到最后就是考驗(yàn)一個(gè)人的數(shù)學(xué)能力,交并集、邏輯思維、算法等都能看到數(shù)學(xué)的身影。最后生成簽名如下:
生成PDF文檔
html2canvas是一款將HTML代碼轉(zhuǎn)換成Canvas的插件,因此需要用一個(gè)div包裹住需要打印的內(nèi)容區(qū)域,獲得這個(gè)dom節(jié)點(diǎn)。
html2Canvas(dom,?{????allowTaint:?true,
????width:?dom.offsetWidth,?//設(shè)置獲取到的canvas寬度
????height:?dom.offsetHeight,?//設(shè)置獲取到的canvas高度
????x:?0,?//頁面在水平方向滾動(dòng)的距離
????y:?0,?//頁面在垂直方向滾動(dòng)的距離
???})
注意:此處需要設(shè)置width和height及x,y,否則當(dāng)頁面內(nèi)容只有一頁的時(shí)候沒有問題,但是若頁面內(nèi)容有很多頁的時(shí)候,就會(huì)出現(xiàn)生成的圖片只有一小部分有內(nèi)容的現(xiàn)象。問題就出現(xiàn)在這個(gè)配置參數(shù)上,若沒有設(shè)置寬高,則默認(rèn)只取當(dāng)前視口的內(nèi)容,丟棄掉其他超出當(dāng)前視口的內(nèi)容。
設(shè)置打印參數(shù):
????html2Canvas(dom,?{
??????allowTaint:?true,
??????width:?dom.offsetWidth,?//設(shè)置獲取到的canvas寬度
??????height:?dom.offsetHeight,?//設(shè)置獲取到的canvas高度
??????x:?0,?//頁面在水平方向滾動(dòng)的距離
??????y:?0,?//頁面在垂直方向滾動(dòng)的距離
????}).then((canvas:?HTMLCanvasElement)?=>?{let?canvasWidth?=?canvas.width;let?canvasHeight?=?canvas.height;let?pageHeight?=?(canvasWidth?/?592.28)?*?841.89;?//?一頁A4?pdf能顯示的canvas高度let?imgWidth?=?595.28;?//?設(shè)置圖片寬度和A4紙寬度相等let?imgHeight?=?(592.28?/?canvasWidth)?*?canvasHeight;//等比例換算成A4紙的高度let?totalHeight?=?imgHeight;?//?需要打印的圖片總高度,初始狀態(tài)和圖片高度相等let?pageData?=?canvas.toDataURL('image/png',?1.0);let?PDF?=?new?JsPDF('p',?'pt',?'a4',?true);if?(totalHeight?????????PDF.addImage(pageData,?'JPEG',?0,?0,?imgWidth,?imgHeight);?//?從頂部開始打印
??????}?else?{let?top?=?0;???//?打印初始區(qū)域while?(totalHeight?>?0)?{
??????????PDF.addImage(pageData,?'JPEG',?0,?top,?imgWidth,?imgHeight);??//?從圖片頂部往下top位置開始打印
??????????totalHeight?-=?pageHeight;
??????????top?-=?841.89;if?(totalHeight?>?0)?{
????????????PDF.addPage();
??????????}
????????}
??????}
??????PDF.save('test.pdf');
????});
??};
選擇分頁位置
按照上述步驟生成了一份PDF文檔,但是當(dāng)PDF頁數(shù)有很多的時(shí)候,會(huì)有這樣的問題?可以看到,分頁的時(shí)候從這段文字這里懶腰截?cái)嗔恕_@顯然不是我們想要看到的效果,如何解決這個(gè)問題呢??
- PDF文檔頁數(shù)較少的情況
可以在開發(fā)測(cè)試的時(shí)候預(yù)先在將要分頁的地方插入一個(gè)padding,就是提前預(yù)留分頁位置
- PDF文檔頁數(shù)較多
對(duì)于這種情況,筆者嘗試遍歷要打印的dom節(jié)點(diǎn)的子節(jié)點(diǎn),將每一頁所能打印的dom節(jié)點(diǎn)高度累加,若超過了頁面所能承載的最大高度,則將最后一個(gè)節(jié)點(diǎn)增加padding,打印完畢將樣式還原。這種方法因?yàn)橐?jì)算每個(gè)dom節(jié)點(diǎn)的高度,非常耗性能,也要求頁面dom元素的顆粒度較細(xì),否則會(huì)出現(xiàn)一個(gè)頁面有大塊空白,完全無法模擬出word生成pdf的那種效果,所以就不展開討論了。如若有讀者有比較好的解放方案,歡迎不吝賜教,感謝~??
推薦閱讀??點(diǎn)擊標(biāo)題可跳轉(zhuǎn)1、燒腦!JS+Canvas 帶你體驗(yàn)「偶消奇不消」的智商挑戰(zhàn)
2、canvas 中普通動(dòng)效與粒子動(dòng)效的實(shí)現(xiàn)
3、基于 HTML5 Canvas 的交互式地鐵線路圖
覺得本文對(duì)你有幫助?請(qǐng)分享給更多人
推薦關(guān)注「前端大全」,提升前端技能
點(diǎn)贊和在看就是最大的支持??
總結(jié)
以上是生活随笔為你收集整理的app canvas渲染后图片黑色_H5 基于 canvas 实现电子签名并生成PDF文档的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 友盟小米收不到推送消息_Android
- 下一篇: linux执行.sql脚本 db2,DB