最近寫專案有用到html2canvas.js,可以實現頁面的截圖功能,但遭遇了許多的坑,特此寫一篇隨筆記錄一下。
使用html2canvas時可能會遇到諸如只能截取視覺化介面、截圖沒有背景色、svg標籤無法截取等問題,以下詳細的說明一下。
一、導入html2canvas.js這不需要多說,可以從github取得:https://github.com/niklasvh/html2canvas
也可以直接匯入連結: <script src=https://cdn.bootcss.com/html2canvas/0.5.0-beta4/html2canvas.js></script>
使用起來也非常簡單,特定的API可以去網上查找,生成png圖片使用image/png即可。
其中$(#xxx)為你想要截取的div,外面可以透過jquery取得它,當然document取得也是可以的。
html2canvas($(#xxx), { onrendered: function (canvas) { var url = canvas.toDataURL(image/png); window.location.href = url; } });
其它類型的圖片如jpg,為image/jpeg等等,可自行查詢API。
到這裡其實簡單的截圖已經完成了,如果介面稍微複雜一點的話,可能就會出現各種坑,下面一個一個解決。
二、svg無法截取的問題當我們截取一個div時,如果這個div有svg標籤,一般情況下是截取不到的,例如截取一個流程圖,得到的是下面這個樣子:
可以看到,流程圖的線沒有截取到,也就是svg沒有截取到,這時的解決方法是把svg轉換成canvas再進行截圖即可,直接上程式碼。
這裡的each循環是循環所有的svg標籤,將它們全部轉換為canvas
if (typeof html2canvas !== 'undefined') { //以下是對svg的處理var nodesToRecover = []; var nodesToRemove = []; var svgElem = cloneDom.find('svg'); svgElem.each(unction (. index, node) { var parentNode = node.parentNode; var svg = node.outerHTML.trim(); var canvas = document.createElement('canvas'); canvas.width = 650; canvas.height = 798; canvg(canvas, svg); if (node.style.position) { canvasposition) { canvasposition) { canvas.position) { canvas.position) { .style.position += node.style.position; canvas.style.left += node.style.left; canvas.style.top += node.style.top; } nodesToRecover.push({ parent: parentNode, child: node }); parentNode.removeChild(node); nodesToRemove.push({ parentNode.removeChild(node); nodesToRemove.push({ parent: parent: parent: parent , child: canvas }); parentNode.appendChild(canvas); }); }
這裡要用到canvg.js,以及它的依賴檔rgbcolor.js,網路上可以直接下載,也可以直接匯入。
三、背景透明的問題這其實很簡單,因為它預設是透明的,html2canvas中有一個參數background就可以加入背景色,如下:
html2canvas(cloneDom, { onrendered: function(canvas) { var url =canvas.toDataURL(image/png); }, background:#fafafa});四、只能截取可視部分的問題
如果需要截取的div超出了介面,可能會遇到截取不全的問題,如上圖,只有一半的內容,這是因為看不到的部分被隱藏了,而html2canvas是無法截取隱藏的dom的。
所以此時的解決方法是使用克隆,將需要截取的部分克隆一份放在頁面底層,再使用html2canvas截取這個完整的div,截取完成後再remove這部分內容即可,完整程式碼如下:
function showQRCode() { scrollTo(0, 0); //複製節點,預設為false,即不複製方法屬性,為true是全部複製。 var cloneDom = $(#d1).clone(true); //設定複製節點的z-index屬性,只要比被複製的節點層級低即可。 cloneDom.css({ background-color: #fafafa, position: absolute, top: 0px, z-index: -1, height: 798, width: 650 }); if (typeof html2canvas !== 'undefined') { / /以下是對svg的處理var nodesToRecover = []; var nodesToRemove = []; var svgElem = cloneDom.find('svg');//divReport為需要截取成圖片的dom的id svgElem.each(function (index, node) { var parentNode = node.parentNode; var svg = node. outerHTML.trim(); var canvas = document.createElement('canvas'); canvas.width = 650; canvas.height = 798; canvg(canvas, svg); if (node.style.position) { canvas.style.position += node.style.position; canvas.style.left += node. style.left; canvas.style.top += node.style.top; } nodesToRecover.push({ parent: parentNode, child: node }); parentNode.removeChild(node); nodesToRemove.push({ parent: parentNode, child: canvas }); parentNode.appendChild(canvas); }); //將複製節點動態追加到複製節點動態追蹤到body後面。 $(body).append(cloneDom); html2canvas(cloneDom, { onrendered: function(canvas) { var url =canvas.toDataURL(image/png); window.location.href = url ; cloneDom.remove(png); //清空克隆的內容}, background:#fafafa }); } }
這裡外面首先將要截取的div克隆一份,並將z-index設置為最小,避免引起界面的不美觀,然後是對svg進行的處理,上面已經分析過了,最後將克隆節點追加到body後面即可。
在onrendered中,我們可以直接使用location.href跳轉查看圖片,可以進行保存操作,也可以將url寫入img的src中顯示在介面上,如$('#imgId').attr('src' ,url);
最後可以在介面展示剛剛被截取的圖片:
五、上傳圖片儲存到資料庫,並在介面中取得該圖片顯示現在得到url了,需要上傳到後端,並儲存到資料庫中,再另一個展示的介面中載入該圖片。我通常習慣使用url來儲存圖片路徑,而不是用blob儲存。
因為需要在另一個介面中取得圖片,所以我把圖片存在了一個與webapp同級的一個resource目錄下,程式碼如下:
//儲存圖片並回傳圖片路徑BASE64Decoder decoder = new BASE64Decoder(); byte[] b = decoder.decodeBuffer(product.getProPic().substring(data:image/png;base64,.length())); Bytesubstring(data:image/png;base64,.length())); Bytesubstring(Stream bais = new ByteArrayInputStream(b); BufferedImage bi1 = ImageIO.read(bais); String url = user_resource + File.separator + img + File.separator + product_+UUID.randomUUID().toString().replace(-, )+.png; String totalUrl = System.getProperty( root) + url; File w2 = new File(totalUrl); ImageIO.write(bi1, png, w2); product.setProPic(url); //將圖片的相對路徑儲存到資料庫中int res = productMapper.insertSelective(product); //加入資料庫
這裡因為涉及到其它邏輯,所以只放一部分程式碼。
這裡使用的是BASE64Decoder來儲存圖片,我們取得到圖片後,需要使用substring將data: url.substring(data:image/png;base64,.length())
image/png url.substring(data:image/png;base64,.length())
。
對於路徑,上面程式碼中的url是我儲存到資料庫中的內容,而totalUrl就是實際進行ImageIO的write操作時儲存的真實路徑,getProperty()方法取得的專案的根目錄,可以在web.xml中配置如下內容,然後System.getProperty(root) 即可。
<!-- 設定係統取得專案根目錄--><context-param> <param-name>webAppRootKey</param-name> <param-value>root</param-value></context-param><listener > <listener-class> org.springframework.web.util.WebAppRootListener </listener-class></listener>
現在圖片的url就存到資料庫裡了,而圖片本身就儲存在tomcat下該項目的這個目錄下。
最後外面在介面上獲取,只需要在當前的url前面加上項目名即可< img class =depot-img src =<%=request.getContextPath()%>/`+e.proPic+` >
。
然後就可以看到介面上顯示的圖片了:
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。