簡體   English   中英

使用canvas.toDataURL時如何設置crossOrigin屬性?

[英]How do I set crossOrigin attribute when using canvas.toDataURL?

所以我正在嘗試為我正在構建的OpenLayers 3應用程序創建打印映射函數。 我知道他們的例子,但每當我嘗試使用它時,我都遇到了可怕的污點畫布問題。 我已經閱讀了整個互聯網並且遇到了人們首先正確設置CORS(完成和完成)但也要做:

          var img = new Image();
          img.setAttribute('crossOrigin', 'anonymous');
          img.src = url;

以上描述了在這里

我的問題是,我以前從未真正使用過toDataURL(),而且我不確定如何確保正在創建的圖像在被激活之前正確設置了crossOrigin屬性:

Error: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

有什么想法嗎?

我見過這個 我的問題是他們如何將其納入一個有效的功能。 就像是:

    var printMap = function(){
     var img = new Image();
     img.setAttribute('crossOrigin', 'anonymous');
     img.src = url;
     img.onload = function() {
      var canvas = document.getElementsByTagName('canvas');
      var dataURL = canvas.toDataURL("image/png");
      console.log(dataURL);
     };
   };

如果瀏覽器支持crossOrigin屬性/屬性(它現在在FF,Chrome,最新的Safari和Edge中) ,但是服務器沒有使用正確的標題回答Access-Control-Allow-Origin: * ),那么img的onerror事件引發了。

因此,如果我們想要繪制圖像,我們可以只處理此事件並刪除屬性。
對於不處理此屬性的瀏覽器,測試畫布是否被污染的唯一方法是將toDataURL調用到try catch塊中。

這是一個例子:

 var urls = ["http://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png", "http://lorempixel.com/200/200"]; var tainted = false; var img = new Image(); img.crossOrigin = 'anonymous'; var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); document.body.appendChild(canvas); var load_handler = function() { canvas.width = 200; canvas.height = 200; ctx.fillStyle = 'white'; ctx.font = '15px sans-serif'; ctx.drawImage(this, 0, 0, 200, 200*(this.height/this.width)); // for browsers supporting the crossOrigin attribute if (tainted) { ctx.strokeText('canvas tainted', 20, 100); ctx.fillText('canvas tainted', 20, 100); } else { // for others try { canvas.toDataURL(); } catch (e) { tainted = true; ctx.strokeText('canvas tainted after try catch', 20, 100); ctx.fillText('canvas tainted after try catch', 20, 100); } } }; var error_handler = function() { // remove this onerror listener to avoid an infinite loop this.onerror = function() { return false }; // certainly that the canvas was tainted tainted = true; // we need to removeAttribute() since chrome doesn't like the property=undefined way... this.removeAttribute('crossorigin'); this.src = this.src; }; img.onload = load_handler; img.onerror = error_handler; img.src = urls[0]; btn.onclick = function() { // reset the flag tainted = false; // we need to create a new canvas, or it will keep its marked as tainted flag // try to comment the 3 next lines and switch multiple times the src to see what I mean ctx = canvas.cloneNode(true).getContext('2d'); canvas.parentNode.replaceChild(ctx.canvas, canvas); canvas = ctx.canvas; // reset the attributes and error handler img.crossOrigin = 'anonymous'; img.onerror = error_handler; img.src = urls[+!urls.indexOf(img.src)]; }; 
 <button id="btn"> change image src </button><br> 

但是因為toDataURL只是一個非常重要的調用,並且try catch中的代碼被去優化,對於舊版瀏覽器來說,更好的替代方法是創建1px * 1px測試器畫布,首先在其上繪制圖像並在其中調用其toDataURL try-catch塊:

 var urls = ["http://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png", "http://lorempixel.com/200/200"]; var img = new Image(); img.crossOrigin = 'anonymous'; var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); document.body.appendChild(canvas); //create a canvas only for testing if our images will taint our canvas or not; var taintTester = document.createElement('canvas').getContext('2d'); taintTester.width = 1; taintTester.height = 1; var load_handler = function() { // our image flag var willTaint = false; // first draw on the tester taintTester.drawImage(this, 0, 0); // since it's only one pixel wide, toDataURL is way faster try { taintTester.canvas.toDataURL(); } catch (e) { // update our flag willTaint = true; } // it will taint the canvas if (willTaint) { // reset our tester taintTester = taintTester.canvas.cloneNode(1).getContext('2d'); // do something ctx.fillStyle = 'rgba(0,0,0,.7)'; ctx.fillRect(0, 75, ctx.measureText('we won\\'t diplay ' + this.src).width + 40, 60); ctx.fillStyle = 'white'; ctx.font = '15px sans-serif'; ctx.fillText('we won\\'t diplay ' + this.src, 20, 100); ctx.fillText('canvas would have been tainted', 20, 120); } else { // all clear canvas.width = this.width; canvas.height = this.height; ctx.fillStyle = 'white'; ctx.font = '15px sans-serif'; ctx.drawImage(this, 0, 0); } }; var error_handler = function() { // remove this onerror listener to avoid an infinite loop this.onerror = function() { return false }; // we need to removeAttribute() since chrome doesn't like the property=undefined way... this.removeAttribute('crossorigin'); this.src = this.src; }; img.onload = load_handler; img.onerror = error_handler; img.src = urls[0]; btn.onclick = function() { // reset the attributes and error handler img.crossOrigin = 'anonymous'; img.onerror = error_handler; img.src = urls[+!urls.indexOf(img.src)]; }; 
 <button id="btn">change image src</button> 

注意

跨源請求不是污染畫布的唯一方法:
在IE <Edge中,在畫布上繪制svg會使畫布受到安全問題的污染,同樣地,如果在畫布上繪制的svg中存在<foreignObject> ,則最新的Safari會對畫布進行污染,最后,任何UA都會如果塗上其他受污染的畫布,則會污染畫布。

因此,在這些情況下檢查畫布是否被污染的唯一解決方案是try-catch ,最好是在1px x 1xx測試畫布上執行此操作。

所以Pointy和Kaiido都有有效的方法來完成這項工作,但他們都錯過了這是一個OpenLayers問題(在Pointy的情況下,不是一個重復的問題)。

答案是這樣做:

            source = new ol.source.TileWMS({
              crossOrigin: 'anonymous'
            });

基本上你必須告訴地圖和你想要的圖層crossOrigin:匿名。 否則你的畫布仍然會被污染。 你知道的越多!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM