這是項目中遇到的一個問題,起初以爲把一個html元素生成圖片,提供給用戶下載的需求挺容易實現的,畢竟看過一些截圖的插件,可是在真正操做中遇到了各類各樣的問題,好比移動端上截圖顯示不清晰,html元素中含有跨域的img圖片致使污染canvas等等等。。到如今我尚未真正解決這裏的一些問題,先暫時記錄一下使用的狀況吧;html
以下圖,將一個包含有文字、圖片的元素生成一張圖片;
html2canvas + canvas2Image
起初以爲使用html2canvas仍是挺簡單的,只須要html2canvas(DOM)就能解決,可是遇到的問題並沒那麼簡單;
一、首先需求是在移動的中使用,這就遇到了Retina的問題;
如今的手機屏幕大多數都是分辨率爲2,也就是原來的兩個像素到了手機上成一個像素顯示,這也致使了canvas截得的圖在移動端上顯示變得很模糊;
正常的處理方式固然是將圖片放大分辨率的倍數,而後再放入原大小的元素中;
網上看了不少對於該問題的方法,可是沒有一個徹底解決的,但有 大神github Demo,很大的解決了個人問題;
修改了兩處代碼(具體修改請見大神github Demo),主要是用於放大和設置對應大小的canvas,已經能夠很完美的解決圖片模糊的問題了
可是!!!!! 個人移動端頁面是試用rem進行佈局,rem值不定致使canvas繪圖區域的寬高計算錯誤,沒法完整包含整個元素;
最後的解決辦法: 在調用html2canvas的時候,canvas.width 和 canvas.height 設置爲一個遠大於元素寬度的值,使其canvas的繪圖區域等於canvas的大小,不會出現截圖不完整的狀況前端
var main = { var main = { init:function(){ main.setListener(); }, //設置監聽事件 setListener:function(){ main.html2Canvas(); }, //獲取像素密度 getPixelRatio:function(context){ var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1; return (window.devicePixelRatio || 1) / backingStore; }, //繪製dom 元素,生成截圖canvas html2Canvas: function () { var shareContent = $("#view")[0];// 須要繪製的部分的 (原生)dom 對象 ,注意容器的寬度不要使用百分比,使用固定寬度,避免縮放問題 var width = shareContent.offsetWidth; // 獲取(原生)dom 寬度 var height = shareContent.offsetHeight; // 獲取(原生)dom 高 var offsetTop = shareContent.offsetTop + 999; //元素距離頂部的偏移量 var canvas = document.createElement('canvas'); //建立canvas 對象 var context = canvas.getContext('2d'); var scaleBy = main.getPixelRatio(context); //獲取像素密度的方法 (也能夠採用自定義縮放比例) canvas.width = width * scaleBy + 999; //這裏 因爲繪製的dom 爲固定寬度,居中,因此沒有偏移 canvas.height = (height + offsetTop) * scaleBy; // 注意高度問題,因爲頂部有個距離因此要加上頂部的距離,解決圖像高度偏移問題 context.scale(scaleBy, scaleBy); var opts = { // allowTaint:true, // 容許加載跨域的圖片 useCORS:true, tainttest:true, //檢測每張圖片都已經加載完成 scale:scaleBy, // 添加的scale 參數 canvas:canvas, //自定義 canvas logging: true, //日誌開關,發佈的時候記得改爲false width:width, //dom 原始寬度 height:height //dom 原始高度 }; html2canvas(shareContent, opts).then(function (canvas) { var img = Canvas2Image.convertToImage(canvas, canvas.width, canvas.height); var body = document.getElementsByTagName("body"); // body[0].appendChild(canvas); // body[0].appendChild(img); $('#view').html(img); img.setAttribute('width', width); img.setAttribute('height', height); }); setTimeout(function() { $('.loadingbox').fadeOut(1500); }); } }; //最後運行代碼 // setTimeout(function() { main.init(); // }, 1000);
二、html元素中包含外部域名的圖片,致使canvas沒法截圖:
解決方式:
①、使用官方提供的proxy代理,須要後端;官方解決圖片跨域方式,
②、修改html2canvas源碼,對跨域圖片作特殊處理git
function ImageContainer(src, cors) { this.src = src; this.image = new Image(); var self = this; var loation = location.hostname; //獲取主域名 var syc = this.src.indexOf(loation) < 0 && this.src.indexOf('data:image/png;base64') < 0; //用於區分是不是跨域的圖片 this.tainted = null; this.promise = new Promise(function(resolve, reject) { self.image.onload = resolve; self.image.onerror = reject; if (cors) { if (syc) { self.image.crossOrigin = ""; //跨域圖片處理1.去掉此處的anonymous代碼改成空 } else { self.image.crossOrigin = "anonymous"; }; } syc? self.image.src = src+"?"+new Date().getTime() : self.image.src = src; //跨域圖片處理2.在源src後面添加一個隨機數如時間戳 if (self.image.complete === true) { resolve(self.image); } }); }
而後修改html2canvas參數 useCORS:true;//這個地方只須要將useCORS設置成true,千萬不要加allowTaint:true這兩個不要同時加,我當時在看官網文檔的時候,看到這兩個參數都是解決跨域的,後來就兩個都加上去了,發現兩個加上去就會有問題
至此,纔算剛剛能達到需求的要求,可是對於rem不定的佈局中,如何真正準確計算canvas的繪圖區域大小仍是沒有真正的搞懂,只是暫時可以使用,先記錄下來,最後放上整合好的html2canvas.jsgithub
2017.07.06:
關於在截取的元素中插入圖片跨域的問題,因爲公司將圖片保存在其餘雲平臺(如七牛類型的),並設置了域名訪問限制,致使了上面問題2方法無效,即便是以上代碼是容許訪問的域名中執行也不行。。。。因此只能轉換思路,將圖片轉換成base64,可是蛋疼的是前端將線上圖片轉換成base64最多見的方法就是canvas 和 XMLHttpRequest,卻也都沒法請求下來, 都是報Access-Control-Allow-Origin不容許的錯誤;因此。。。。只能讓後端來完成轉成base64了,極度的蛋疼。。。web
2017.08.21:
運營反饋在iphone的微信中,生成的圖片會消失,變成一片白色;通過測試,生成canvas沒有問題,canvas生成img也沒有問題,問題就出在了img插入文本中的時候,原來使用的是$('#view').html(img); #view中包含了原來要生成canvas的dom,多是直接覆蓋出的爲題,最後用兩個div分別包裹dom ;和生成的img,img生成後,隱藏dom,暫時測試沒有問題,不知道上線後會不會有妖蛾子canvas