我們在前端報表中完成了各種工作資料的輸入或內容處理之後,需要做什麼?
資料的匯出!
這些資料的常用匯出格式有:PDF、Excel、HTML和圖片幾大型別。
但總有一些實際應用場景,需要的不僅僅是將現有內容匯出,還需要我們對一些內容的格式進行轉化。
就在前幾天,葡萄剛上班,就看到客戶發來下圖,發生了以下對話
-葡萄,這一頁可以匯出嗎?
-當然可以,PDF、Excel、HTML都可以。
-可是我想把這一頁匯出圖片。
這時候問題就出現了,在我們的前端電子報表中並沒有預設圖片儲存的格式,那這時候我們如何用已有功能進一步擴充套件,來實現這個功能呢?
一、確定實現思路
巧婦難為無米之炊,首先我們先整理一下手中素材。
通過閱讀文件瞭解我們可以自定義新增按鈕:
同時我們還可以在action屬性中,給按鈕定義點選後觸發的事件:
順著這個思路,我們可以在工具欄新增一個匯出按鈕,將按鈕的動作設定為"點選這個按鈕時實現匯出圖片的功能"。ARJS本身支援匯出PDF,並且也提供了直接呼叫匯出PDF的介面:export,所以我們可以先通過介面匯出PDF,然後再將PDF轉換為圖片,最終實現匯出圖片的功能。
這下子,我們的最終問題就變成了是如何 將 PDF 轉換為圖片並匯出 。
PDF.js是一款使用HTML5 Canvas安全地渲染PDF檔案以及遵從網頁標準的網頁瀏覽器渲染PDF檔案的JavaScript庫。我們可以通過PDF.js庫將匯出的PDF通過Canvas在網頁上渲染出來,然後通過Canvas的toDataURL方法返回一個包含圖片展示的 data URL。拿到這個URL就好辦了,可以利用a標籤的download屬性直接對其進行下載,最終實現在ARJS中匯出圖片的功能。
總結,整體實現思路如下:
- 新增匯出圖片按鈕
- 實現匯出PDF
- 將 PDF 通過 PDF.js 庫渲染成 <canvas>
- 通過a標籤的download屬性將<canvas>儲存為圖片
二、程式碼實戰
簡單起見,本示例不使用任何框架整合ARJS,選擇在純JaveScript中整合報表,大家可以閱讀相關文件:在純JavaScript專案中整合報表 Viewer。另外,為了在document中插入canvas元素,事先可以建立一個div元素,以便之後在該節點下插入canvas元素;同時為了介面中只有報表檢視器,可以隱藏該div。最終的頁面結構如下:
<body>
<div id="viewer-host"></div>
<div id="imgDiv" style="display: none"></div>
</body>
新增匯出圖片按鈕
1. let exportImageButton = {
2. key: '$exportImage',
3. icon:{
4. type: 'svg',
5. content:'<svg role="img" xmlns="http://www.w3.org/2000/svg" width="21px" height="21px" viewBox="0 0 24 24" aria-labelledby="imageIconTitle" stroke="#205F78" stroke-width="2.2857142857142856" stroke-linecap="square" stroke-linejoin="miter" fill="none" color="#205F78"> <title id="imageIconTitle">Image</title> <rect width="18" height="18" x="3" y="3"/> <path stroke-linecap="round" d="M3 14l4-4 11 11"/> <circle cx="13.5" cy="7.5" r="2.5"/> <path stroke-linecap="round" d="M13.5 16.5L21 9"/> </svg>',
6. size: 'small'
7. },
8. enabled: true,
9. title:'匯出圖片',
10. action: function() {
11. //定義匯出圖片按鈕點選事件
12. }
13. };
14. viewer.toolbar.addItem(exportImageButton);
介面文件:addItem。
(提示:以上在icon 的content的屬性中,使用了一個svg,這個示例程式碼中的svg來自網站:ikonate 。如果大家有需要可自行下載,如果作為商用需要注意版權 )
以上程式碼新增之後,我們就可以在報表預覽介面的工具欄看到這樣一個按鈕:
實現匯出PDF
在exportImageButton的action中定義一個exportImage方法,在這個方法中首先實現匯出PDF,匯出的結果包含一個PDF檔案的blob物件,大家可自行列印出來看一下匯出結果:
function exportImage() {
const settings = { title: 'Active Reports JS' };
viewer.export('PDF', settings).then((result) =>{
//這個result包含一個所匯出PDF的blob物件
console.log(result);
});
}
將PDF通過PDF.js庫渲染成canvas
首先我們需要去PDF.js官網下載相關檔案引入到專案中,我這裡的示例通過cdn的方式引入:
1. <script src="https://cdnjs.cloudflare.com/ajax/libs/PDF.js/2.10.377/PDF.min.js"></script>
引入之後,我們就可以對上一步得到的blob物件進行操作,將PDF渲染成\<canvas\>:
function pageToCanvasObj(page) {
const viewport = page.getViewport({scale: 1});
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
canvas.style.width = "100%";
canvas.style.height = "100%";
imgDiv.append(canvas);
return {
canvas,
renderContext: {
canvasContext: context,
viewport
}
}
}
function exportImage() {
const settings = { title: 'Active Reports JS' };
viewer.export('PDF', settings).then((result) =>{
//核心程式碼
//通過FileReader的介面將blob轉換為ArrayBuffer
const fileReader = new FileReader();
fileReader.readAsArrayBuffer(result.data);
fileReader.onload = function() {
//為了讀寫ArrayBuffer物件,建立typedArray檢視
const typedArrayResult = new Uint8Array(fileReader.result);
//PDF.js讀取文件後渲染canvas
PDFjsLib.getDocument(typedArrayResult).promise.then(function(PDF) {
if (PDF) {
const pageNum = PDF.numPages;
for (let i = 1; i <= pageNum; i++) {
PDF.getPage(i).then((page) => {
//建立canvas,並且返回相關資料
const canvasObj = pageToCanvasObj(page);
//<canvas>渲染
page.render(canvasObj.renderContext).promise.then(() => {
//通過canvas物件的toDataURL得到圖片連結
const imgUrl = canvasObj.canvas.toDataURL();
})
})
}
}
},(error) => {
alert(error);
});
};
});
}
通過a標籤的download屬性將canvas儲存為圖片
將上一步得到的imgURL通過a標籤下載:
function saveImage(index, url) {
const link = document.createElement("a");
link.href = url;
link.download = `image${index}`;
link.click();
link.remove();
}
這下就實現了在在前端報表中完整將報表內容作為圖片匯出。在此附上示例完整demo程式碼檔案:
https://gcdn.grapecity.com.cn...;aid=MTY0Njg4fGNlMzM5MTkwfDE2MzM2NjU4MzB8NjI2NzZ8MTMyNDM3
匯出效果:
到這裡,已經完全解決了本次提到的問題~
後續也會為大家帶來更多有趣或嚴肅的內容。