如果画布元素已被污染,如何检查?

时间:2014-03-22 09:12:11

标签: javascript html5-canvas cors

基本情景

我在客户端加载了几张图片。其中一些来自另一个域,有些则不是。有些我可以使用crossOrigin attribute访问,有些则无法访问。

基本要求是尽可能为图像检索dataURL

问题

将图像绘制到canvas元素后(我需要获取dataURL,对吗?),如何在没有try ... catch块的情况下检查画布是否已被污染< / em>的?如果画布受到污染,我将无法再使用toDataURL()see 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

2 个答案:

答案 0 :(得分:4)

这是一个不向本机对象添加属性的解决方案:

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的解决方案。但是,这是检查的正确方法,因为没有向用户公开的原始清洁标志(它仅供内部使用)。将属性添加到本机对象建议。

答案 1 :(得分:1)

这是对没有使用try-catch的CORS污染的间接测试:

演示:http://jsfiddle.net/m1erickson/uDt2K/

它的工作原理是在加载图像之前设置image.tainted=true标志

然后在image.onload中,context.getImageData触发/不会触发CORS违规。

如果没有发生违规,则污点标志设置为假(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是异步的,因此即使在CORS违规后,image.onload外部的代码仍会执行。

以下示例代码:

  • 创建图像对象
  • 测试图像是否符合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's tainted
            callWhenTainted(img);
        }else{
            // it's OK, do dataURL stuff
            callWhenOK(img);
        }
    }


}); // end $(function(){});
</script>
</head>
<body>
    <canvas id="canvas" width=360 height=281></canvas>
</body>
</html>