所見即所得 —— HTML轉圖片組件開發

語言: CN / TW / HK

政採雲技術團隊.png

偉豪.png

這是第 160 篇不摻水的原創,想獲取更多原創好文,請搜索公眾號【政採雲前端團隊】關注我們吧~

前言

在我們日常開發中一定會遇到"所見即所得"的需求,如導出查詢表格中的內容為Excel表格——《前端導出Excel,讓後端刮目相看》、通過後台網頁配置實現配置預覽頁與實際頁面展示的統一——《從零開發一款可視化大屏製作平台》

今天我們也來實現一個"所見即所得"的需求:將用户所見網頁提取為圖片。

2C0714D1-BBEB-4810-8C64-F66963489F9D.png

方案1:最短步驟實現結果

第一個想到的方案就是通過瀏覽器自帶的網頁另存為圖片去實現。

68630441-5C95-4594-AFB1-CDE4C6F1B348.png

但這種方法顯然是不可行的。第一需要提示用户操作進行繁瑣的操作,第二無法達到局部提取為圖片的效果。

方案2:達成初步可行方案

通過調研發現,可以使用 html2canvas 將網頁先轉換為 canvas數據。再將其轉換為圖片的方法,最終實現我們想要的功能。

20221011134318.jpg

引入html2canvas

cnpm install --save html2canvas

HTML ```html

名稱:

年齡:

班級: 班級1 班級2

保存為圖片

```

JS

js // 點擊保存為Canvas onSaveCanvas(){ // 這裏的類名要與點擊事件裏的一樣 const canvas = document.querySelector('#screenshot-box'); let that = this; html2canvas(canvas,{scale:2,logging:false,useCORS:true}).then(function(canvas) { const type = 'png'; let imgData = canvas.toDataURL(type); // 圖片格式處理 let _fixType = function(type) { type = type.toLowerCase().replace(/jpg/i, 'jpeg'); let r = type.match(/png|jpeg|bmp|gif/)[0]; return 'image/' + r; }; imgData = imgData.replace(_fixType(type),'image/octet-stream'); let filename = "htmlImg" + '.' + type; // 保存為文件 // 以bolb文件下載 that.downFileToLocal(filename,that.convertBase64ToBlob(imgData)) }); },

如此我們便實現了初步的功能。

1.gif

當然,我們也可以設置一個預覽圖片來預覽我們將要導出的圖片。

HTML

html <img :src="previewPic" alt="預覽圖片"> JS js this.previewPic = URL.createObjectURL(that.convertBase64ToBlob(imgData));

展示效果

A9A4712D-B30C-452E-A610-9F5ACF492162.png

將方案進行拓展並升級

需求止步於此,但秉承着"將事情做的更好"的我們豈能止步於此。

實現HTML導出為Word

我們需要通過 html-docx 來實現導出為Word(導出Word目前只支持原生HTML + CSS)。

引入html-docx

cnpm install --save html-docx-js HTML ```html

姓名 年齡
賈維斯 2
<a-button  @click="onWordExport">導出為word</a-button>

JSjs onWordExport(){ var contentHtml = document.getElementById("export-word").innerHTML; const cssHTML = table { width: 200px; border: 1px solid #ccc; color:red; } var content = <!DOCTYPE html><html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <style> ${cssHTML} </style> </head> <body> ${contentHtml} </body> </html> var converted = htmlDocx.asBlob(content,{orientation:"landscape"}); this.downFileToLocal('word文件名.docx',converted) } ```

展示效果

2.gif

如此我們便實現了導出 HTML 為 Word。

實現HTML導出為PDF

目前市面上 HTML 導出 PDF 的實現方式有多種,如jsPDFiTextwkhtmltopdf等。在不同情況下我們應該使用不同的解決方案:

| 方案 | 優點 | 缺點 | 分頁 | 圖片 | 表格 | 鏈接 | 中文 | 特殊字符 | | --- | --- | --- | --- | --- | --- | --- | --- | --- | | jsPDF | 1、整個過程在客户端執行(不需要服務器參與),調用簡單 | 1、生成的pdf為圖片形式,且內容失真 | 支持 | 支持 | 支持 | 不支持 | 支持 | 支持 | | iText | 1、功能基本可以實現,比較靈活 2、生成pdf質量較高 | 1、對html標籤嚴格,少一個結束標籤就會報錯;2、後端實現複雜,服務器需要安裝字體;3、圖片渲染比較複雜 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 | | wkhtmltopdf | 1、調用方式簡單;2、生成pdf質量較高 | 1、服務器需要安裝wkhtmltopdf環境;2、根據網址生成pdf,對於有權限控制的頁面需要在攔截器進行處理 | 支持 | 支持 | 支持 | 支持 | 支持 | 支持 |

今天我們使用在客户端執行(不需要服務器參與)的方式——jsPDF。

導入jsPDF

npm install --save jspdf

HTML html <a-button @click="onPDFExport">導出為PDF</a-button> JS ```js // 導出為PDF onPDFExport(){ const canvas = document.querySelector('#screenshot-box'); html2canvas(canvas).then(function(canvas) { let contentWidth = canvas.width; let contentHeight = canvas.height; //一頁pdf顯示html頁面生成的canvas高度; let pageHeight = contentWidth / 592.28 * 841.89; //未生成pdf的html頁面高度 let leftHeight = contentHeight; //頁面偏移 let position = 0; //a4紙的尺寸[595.28,841.89],html頁面生成的canvas在pdf中圖片的寬高 let imgWidth = 595.28; let imgHeight = 592.28/contentWidth * contentHeight;

        let pageData = canvas.toDataURL('image/jpeg', 1.0);

        let pdf = new jsPDF('', 'pt', 'a4');

        //有兩個高度需要區分,一個是html頁面的實際高度,和生成pdf的頁面高度(841.89)
        //當內容未超過pdf一頁顯示的範圍,無需分頁
        if (leftHeight < pageHeight) {
          pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight );
        } else {
          while(leftHeight > 0) {
              pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
              leftHeight -= pageHeight;
              position -= 841.89;
              //避免添加空白頁
              if(leftHeight > 0) {
                pdf.addPage();
              }
          }
        }
        pdf.save('content.pdf');
  })
}

```

展示效果

3.gif

如此我們便實現了導出 HTML 為 PDF。

將功能封裝為組件

實現一次HTML導出圖片需要寫的代碼太多,很多參數也需要按需定製。是否能夠將其封裝成組件呢?

我們可以通過Vue的插槽將我們導出的內容進行插入

完整組件

```HTML

```

Attributes

| 參數 | 類型 | 説明 | | --- | --- | --- | |id |String |唯一ID,盒子內容將作為導出內容 | |filename |String |導出的文件名稱(不帶文件類型後綴) | |outType | String | 導出文件類型 | |isFile| Boolean |是否導出為文件,true將下載文件,false返回bolb路徑 |

Methods

| 方法 | 名稱 | 説明 | | --- | --- | --- | | onSaveCanvas | 執行導出或保存方法 | 若isFile為true則保存文件,若isFile為false則返回bolb路徑 |

Events

| 方法 | 名稱 | 説明 | | --- | --- | --- | | onExport | 導出方法 | 當觸發導出/下載時會觸發該方法輸出 Bolb路徑 類型String |

使用組件

HTML html <Html2Image ref="html2Image" @onExport="exportPic" > <div class="export-content" style="border: 1px solid #F0F;width:200px"> <div>1</div> <div>2</div> <div>3</div> </div> </Html2Image> <a-button @click="onExportImgByComponent">組件導出為圖片</a-button> JS ```js // 引入組件 import Html2Image from '@/components/Html2Image/Html2Image.vue' // 使用組件 components: { Html2Image },

// methods onExportImgByComponent(){ this.previewPicComponent =this.$refs.html2Image.onSaveCanvas() }, exportPic(baseUrl){ // 賦值導出圖片的blob路徑 this.previewPicComponent = baseUrl; },
```

Q&A

Q.為什麼外網圖片展示不出?

7BFD5507-2620-4EEB-A4AF-144F63441CD8.png

A: 設置 html2canvas 方法中 useCORS 為 true 即可。

AC53AB9C-170A-4CF8-8F1B-2C7724EDC3F6.png

最後要説的

項目地址: https://github.com/FireSmallPanda/vuexDemo.git

HTML導出為圖片組件地址:HTML導出為圖片組件

根據需求目前只封裝了HTML導出為圖片,相信大家可以依葫蘆畫瓢將導出Word和PDF也一併封裝為組件。

參考文章

《前端實現將頁面保存成圖片功能》

《base64字串轉Blob文件流,Blob文件流再下載到本地》

《render-html-to-pdf》

《HTML頁面導出為PDF》

外網圖片來源——《風光攝影精選》

推薦閲讀

探索組件在線預覽和調試

規範升級 NPM 包

你想知道的前後端協作規範都在這了

帶你瞭解 Tree Shaking

厲害!這篇正則表達式竟寫的如此詳盡

開源作品

  • 政採雲前端小報

開源地址 www.zoo.team/openweekly/ (小報官網首頁有微信交流羣)

  • 商品選擇 sku 插件

開源地址 https://github.com/zcy-inc/skuPathFinder-back/

招賢納士

政採雲前端團隊(ZooTeam),一個年輕富有激情和創造力的前端團隊,隸屬於政採雲產品研發部,Base 在風景如畫的杭州。團隊現有 90 餘個前端小夥伴,平均年齡 27 歲,近 4 成是全棧工程師,妥妥的青年風暴團。成員構成既有來自於阿里、網易的“老”兵,也有浙大、中科大、杭電等校的應屆新人。團隊在日常的業務對接之外,還在物料體系、工程平台、搭建平台、性能體驗、雲端應用、數據分析及可視化等方向進行技術探索和實戰,推動並落地了一系列的內部技術產品,持續探索前端技術體系的新邊界。

如果你想改變一直被事折騰,希望開始能折騰事;如果你想改變一直被告誡需要多些想法,卻無從破局;如果你想改變你有能力去做成那個結果,卻不需要你;如果你想改變你想做成的事需要一個團隊去支撐,但沒你帶人的位置;如果你想改變既定的節奏,將會是“5 年工作時間 3 年工作經驗”;如果你想改變本來悟性不錯,但總是有那一層窗户紙的模糊… 如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望參與到隨着業務騰飛的過程,親手推動一個有着深入的業務理解、完善的技術體系、技術創造價值、影響力外溢的前端團隊的成長曆程,我覺得我們該聊聊。任何時間,等着你寫點什麼,發給 [email protected]