日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

app canvas渲染后图片黑色_H5 基于 canvas 实现电子签名并生成PDF文档

發(fā)布時(shí)間:2025/3/11 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 app canvas渲染后图片黑色_H5 基于 canvas 实现电子签名并生成PDF文档 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

(給前端大全加星標(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)思路

  • 使用canvas來實(shí)現(xiàn)手寫簽名的功能,然后將canvas轉(zhuǎn)化為圖片,貼在簽名的位置;
  • 將整個(gè)需要生成文檔的dom區(qū)域使用html2canvas插件轉(zhuǎn)成一張大圖;
  • 使用JsPDF插件將上述圖片生成PDF文檔;
  • 對(duì)于文件內(nèi)容較多的情況,需要合理選擇分頁位置;
  • 生成簽名

    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;
    ????});

    注意:

  • 在注冊(cè)“touchstart”和“touchmove”事件時(shí),需要阻止默認(rèn)事件,否則頁面會(huì)跟著手勢(shì)上下滑動(dòng)。
  • 移動(dòng)端的每個(gè)觸摸事件對(duì)象中都包括了touches這個(gè)屬性,它用于描述位于屏幕上的所有手指的一個(gè)列表,獲取當(dāng)前事件對(duì)象我們習(xí)慣性的使用event = event.touches[0],而在PC端則不需要這么操作。
  • offsetLeft值跟offsetTop值跟父級(jí)元素沒關(guān)系,而是跟其上一級(jí)的定位元素(除position:static外的所有定位如fixed,relative,absolute元素)有關(guān)系。若上一級(jí)定位元素都沒有除position:staice外的定位,則這個(gè)偏移量是相對(duì)于body而言的。
  • 需要理清移動(dòng)端事件對(duì)象的幾個(gè)屬性,?
  • 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ì)算即可。

  • 在簽名(touchmove)這個(gè)動(dòng)作過程中,我們需要不斷的更新起點(diǎn)位置,否則畫出來是這樣?
  • 其實(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ù):

    const?print?=?()?=>?{let?dom:?HTMLElement?=?pdfDom.current;
    ????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)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。