问题描述
所以我正在尝试为我正在构建的OpenLayers 3应用程序创建打印映射函数。我知道他们的,但每当我尝试使用它我遇到了可怕的污点帆布问题。我已经阅读了整个互联网并遇到了人们首先正确设置CORS(完成和完成)但也要做:
So I'm trying to create a print map function for an OpenLayers 3 application I'm building. I'm aware of their example but whenever I attempt to use it I run into the dreaded tainted canvas issue. I've read the whole internet and come across folks saying first to set CORS correctly (done and done) but also to do:
var img = new Image(); img.setAttribute('crossOrigin', 'anonymous'); img.src = url;
以上描述。
我的问题是,我是从来没有真正使用过toDataURL()之前我并不确定如何确保正在创建的图像在它被激活之前正确设置了crossOrigin属性:
My question is, I've never really used toDataURL() before and I'm not really sure how I make sure the image being created has the crossOrigin attribute correctly set before it slams into the:
Error: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
有什么想法吗?
我见过。我的问题是他们如何将其纳入一个有效的功能。类似于:
I have seen this. My question is how they incorporate that into a function that works. Something like:
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)支持code>属性/属性,但是服务器没有使用正确的标头回答( Access-Control-Allow-Origin:* ),然后触发img的 onerror 事件。
If the crossOrigin property/attribute is supported by the browser (it is now in FF, Chrome, latest Safari and Edge ), but the server doesn't answer with the proper headers (Access-Control-Allow-Origin: *), then the img's onerror event fires.
因此,如果我们想要绘制图像,我们可以处理此事件并删除属性。
对于不处理此事件的浏览器属性,测试画布是否被污染的唯一方法是将 toDataURL 调用到try catch块中。
So we can just handle this event and remove the attribute if we want to draw the image anyway.
For browsers that don't handle this attribute, the only way o test if the canvas is tainted is to call the toDataURL into a try catch block.
以下是一个例子:
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测试器画布,首先在它上面绘制图像并在try-catch块中调用它的toDataURL:
But since toDataURL can be a really heavy call for just a check and that code in try catch is deoptimized, a better alternative for older browsers is to create a 1px*1px tester canvas, draw the images on it first and call its toDataURL in the try-catch block :
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<边缘,在画布上绘制一个svg会污染画布以解决安全问题,同样,如果在一个< foreignObject> 中存在< foreignObject> ,最新的Safari会对画布进行污染svg画在画布上,最后,任何UA都会污染画布,如果涂上另一个受污染的画布。
Cross-origin requests are not the only way to taint a canvas :
In IE < Edge, drawing an svg on the canvas will taint the canvas for security issues, in the same way, latest Safari does taint the canvas if a <foreignObject> is present in an svg drawn onto the canvas and last, any UA will taint the canvas if an other tainted canvas is painted to it.
那么在这种情况下唯一的解决方案是检查是否canvas被污染的是 try-catch ,最好的是在1px xpx测试画布上这样做。
So the only solution in those cases to check if the canvas is tainted is to try-catch, and the best is to do so on a 1px by 1px test canvas.
这篇关于使用canvas.toDataURL时如何设置crossOrigin属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!