Как проверить, испорчен ли элемент холста?
Основной сценарий
Я загружаю несколько изображений на стороне клиента. Некоторые из них из другого домена, некоторые - нет. Некоторые из них могут быть доступны с помощью атрибута crossOrigin
, некоторые из них не являются.
Основным требованием является получение dataURL для изображений, где это возможно.
Вопрос
После рисования изображения в элемент canvas (который мне нужно, чтобы получить dataURL, правильно?), как я могу проверить без блока try ... catch
, было ли пятно испорчено? Если полотно испорчено, я больше не могу использовать toDataURL()
(см. MDN).
var image = new Image(),
canvas = document.createElement( 'canvas' ),
context = canvas.getContext( '2d' );
image.onload = function(){
// adjust dimensions of canvas
canvas.width = image.width;
canvas.height = image.height;
// insert image
context.drawImage( image, 0, 0 );
// how to check here?
// get dataurl
dataurl = tmpCanvas.toDataURL();
// do stuff with the dataurl
};
image.src = src; // some cross origin image
Ответы
Ответ 1
Вот решение, которое не добавляет свойства к собственным объектам:
function isTainted(ctx) {
try {
var pixel = ctx.getImageData(0, 0, 1, 1);
return false;
} catch(err) {
return (err.code === 18);
}
}
Теперь просто проверьте, сделав это:
if (isTainted(ctx)) alert('Sorry, canvas is tainted!');
Изменить: СЕЙЧАС я вижу, что вам нужно решение без try-catch. Тем не менее, это правильный способ проверить, нет ли источника чистого фона, открытого пользователю (только для внутреннего использования). Не рекомендуется добавлять свойства к исходному объекту.
Ответ 2
Здесь косвенный тест на CORS tainting, который не использует try-catch:
A Демо: http://jsfiddle.net/m1erickson/uDt2K/
Он работает, устанавливая флаг image.tainted=true
перед загрузкой изображения
Затем в image.onload, context.getImageData
триггеры/не запускают нарушение CORS.
Если никакого нарушения не происходит, флаг tainted устанавливается на false (image.tainted=false
).
var img=new Image();
// set a "tainted flag to the image to true (initialize as tainted)
img.tainted=true;
img.onload=function(){
// try an action that triggers CORS security
var i=ctx.getImageData(1,1,1,1);
// if we don't get here, we violated CORS and "tainted" remains true
// if we get here, CORS is happy so set the "tainted" flag to false
img.tainted=false;
};
// test with tainted image
img.src="http://pp-group.co.uk/wp/wp-content/uploads/2013/10/house-illustration-web.gif";
Поскольку image.onload является асинхронным, ваш код вне image.onload будет выполняться даже после нарушения CORS.
Вот пример кода, который:
- создает объект изображения
- проверяет, соответствует ли изображение CORS
- выполняет один обратный вызов, если изображение соответствует требованиям.
- выполняет другой обратный вызов, если изображение не соответствует
Пример кода:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
// known CORS violator
var src1="http://pp-group.co.uk/wp/wp-content/uploads/2013/10/house-illustration-web.gif";
// known CORS compliant
var src2="https://dl.dropboxusercontent.com/u/139992952/houseIcon.png";
// callbacks depending on if the image causes tainted canvas
function tainted(img){console.log("tainted:",img.src);}
function notTainted(img){console.log("not tainted:",img.src);}
// testing
var image1=newImage(src1,tainted,notTainted);
var image2=newImage(src2,tainted,notTainted);
function newImage(src,callWhenTainted,callWhenNotTainted){
// tmpCanvas to test CORS
var tmpCanvas=document.createElement("canvas");
var tmpCtx=tmpCanvas.getContext("2d");
// tmpCanvas just tests CORS, so make it 1x1px
tmpCanvas.width=tmpCanvas.height=1;
var img=new Image();
// set the cross origin flag (and cross our fingers!)
img.crossOrigin="anonymous";
img.onload=function(){
// add a tainted property to the image
// (initialize it to true--is tainted)
img.tainted=true;
// draw the img on the temp canvas
tmpCtx.drawImage(img,0,0);
// just in case this onload stops on a CORS error...
// set a timer to call afterOnLoad shortly
setTimeout(function(){
afterOnLoad(img,callWhenTainted,callWhenNotTainted);
},1000); // you can probably use less than 1000ms
// try to violate CORS
var i=tmpCtx.getImageData(1,1,1,1);
// if we get here, CORS is OK so set tainted=false
img.tainted=false;
};
img.src=src;
return(img);
}
// called from image.onload
// at this point the img.tainted flag is correct
function afterOnLoad(img,callWhenTainted,callWhenOK){
if(img.tainted){
// it tainted
callWhenTainted(img);
}else{
// it OK, do dataURL stuff
callWhenOK(img);
}
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=360 height=281></canvas>
</body>
</html>