使用Canvas globalCompositeOperation为拼图游戏后端制作蒙版

时间:2014-04-11 09:47:47

标签: javascript php html5 canvas html5-canvas

我正在尝试发布简单的拼图益智游戏的后端。游戏使用12个预制形状作为制作12个拼图的面具。我找到了优秀的Canvas global CompositeOperation tutorial,并对其进行了测试。

我正在制作的应用程序是使用ajax将每个完成的部分发送到serverside .php-script。用户使用SSE加载图像(600x400),应用程序根据数组的值arr_x和arr_y将原始图像移动到tempCanvas内。它应该在for循环中发生:

function drawPieces(){
    var canvas = document.getElementById("myCanvas");
    var context =canvas.getContext("2d");
    var tempCanvas = document.getElementById("tempCanvas");       
    var tempContext = tempCanvas.getContext("2d");
    var img_mask = new Image();
    var w;
    var h; 
    var cvd0,cvd1,cvd2,cvd3,cvd4,cvd5,cvd6,cvd7,cvd8,cvd9,cvd10,cvd11;
    var arr_data = [cvd0,cvd1,cvd2,cvd3,cvd4,cvd5,cvd6,cvd7,cvd8,cvd9,cvd10,cvd11];
    var arr_x = [0,-136,-289,-414,0,-115,-270,-415,0,-118,-288,-415];
    var arr_y = [0,0,0,0,-113,-111,-111,-124,-244,-256,-243,-241];
    var img_bg = new Image(600, 400);        

    for (var i = 0; i<arr_x.length; i++) {
        img_bg = original;
        // get the mask
        img_mask.src = "img/mask"+i+".png";

        w = img_mask.width;
        h = img_mask.height;
        console.log("w = ", img_mask.width, " h = ", img_mask.height);

        tempCanvas.width = w;
        tempCanvas.height = h;

         // Her lages maska
        tempContext.drawImage(img_mask,0,0);
        tempContext.globalCompositeOperation = 'source-in';
        tempContext.drawImage(img_bg,arr_x[i],arr_y[i]);
        myCanvas.width = w;
        myCanvas.height = h;

        // Draws tempCanvas on to myCanvas
        console.log("tempCanvas: ", tempCanvas, " img_mask.src = ", img_mask.src);
        context.drawImage(tempCanvas, 0, 0);
        arr_data[i] = myCanvas;
        sendData(arr_data[i], [i]);                
    };
}

将图像数据发送到服务器:

function sendData(cvd, index){
    var imageData = cvd.toDataURL("image/png");
    var ajax = new XMLHttpRequest();
    ajax.open("POST", "testsave.php", false);

    // ajax.onreadystatechange=function(){
    //     console.log("index = ", index)
    // };
    ajax.setRequestHeader('Content-Type', 'application/upload');
    ajax.send(imageData+"<split>"+index);      
};

我有一个按钮来启动drawPieces。但是我遇到了很多问题。 Firefox抛出错误:

  

InvalidStateError:尝试使用不可用或不再可用的对象:context.drawImage(tempCanvas,0,0);

但是,如果我再次单击该按钮,则循环运行,我的文件夹中有12个。 (现在使用xammp)。但这些碎片被切错了!每次循环运行时,应用程序似乎都没有加载正确的掩码。

所以我在不使用循环的情况下测试了它,它有12个不同的函数,每个函数调用下一个函数。它使用了一个功能,但是当我添加更多功能时开始搞乱掩码:

function drawPiece0(){
    img_bg = original;
    img_mask.src = "img/mask0.png";
    tempCanvas.width = 185;
    tempCanvas.height = 145;   
     // Her lages maska
    tempContext.drawImage(img_mask,0,0);
    tempContext.globalCompositeOperation = 'source-in';
    tempContext.drawImage(img_bg,0,0);
    myCanvas.width = 185;
    myCanvas.height = 145;
    // Tegner tempCanvas over på myCanvas
    context.drawImage(tempCanvas, 0, 0);
    cvd0 = myCanvas;
    sendData(cvd0, 0);
    // drawPiece1();
};

在我的设置中出现了一些问题,但我无法弄清楚它是什么。有人帮帮我!

顺便说一句,这里也是我的.php脚本:

<?php
if (isset($GLOBALS["HTTP_RAW_POST_DATA"]))
{
    // Get the data
    $imageData=$GLOBALS['HTTP_RAW_POST_DATA'];

    $parts = explode("<split>", $imageData);
    $imageData = $parts[0];
    $index= $parts[1];

    // Remove the headers (data:,) part.
    // A real application should use them according to needs such as to check image type
    $filteredData=substr($imageData, strpos($imageData, ",")+1);

    // Need to decode before saving since the data we received is already base64 encoded
    $unencodedData=base64_decode($filteredData);

    // Save file.
    $fp = fopen( "pieces/".$index.".png", "wb" );
    fwrite( $fp, $unencodedData);
    fclose( $fp );
}
?>

1 个答案:

答案 0 :(得分:0)

问题

问题是图像加载是异步的,这意味着它们会在代码持续时在后台加载。

这意味着当您尝试将它们与drawImage一起使用时,图像不会准备好(加载和解码),从而导致错误。这里的错误是间接的,因为w和h没有得到画布的有效值,这意味着画布将是0宽度和0高度,这将在尝试绘制时触发实际错误。

它&#34;工作的原因&#34;第二次是因为图像存在于浏览器的缓存中,并且可能在使用之前提供图像。

另一个问题是你的循环中你要覆盖图像变量,所以加载时只会使用最后一张图像。

解决方案

解决方案是在开始循环之前制作或使用图像加载器。制作一个很容易但是为了简单起见,我将使用此示例中的YAIL loader(免责声明:作者同上),但只要您对图像使用回调,任何类型的加载器都会执行:

function drawPieces(){
    var canvas = document.getElementById("myCanvas");
    var context =canvas.getContext("2d");
    var tempCanvas = document.getElementById("tempCanvas");       
    var tempContext = tempCanvas.getContext("2d");
    var img_mask;
    var w;
    var h; 
    var cvd0,cvd1,cvd2,cvd3,cvd4,cvd5,cvd6,cvd7,cvd8,cvd9,cvd10,cvd11;
    var arr_data = [cvd0,cvd1,cvd2,cvd3,cvd4,cvd5,cvd6,cvd7,cvd8,cvd9,cvd10,cvd11];
    var arr_x = [0,-136,-289,-414,0,-115,-270,-415,0,-118,-288,-415];
    var arr_y = [0,0,0,0,-113,-111,-111,-124,-244,-256,-243,-241];

    var img_bg;

    // using some image(s) loader
    var loader = new YAIL({done: draw});

    for (var i = 0; i<arr_x.length; i++)
        loader.add("img/mask"+i+".png");

    loader.load();  // start loading images

    // this is called when images has loaded
    function draw(e) {

        for (var i = 0; i<arr_x.length; i++) {

            //img_bg = original;  ??
            // get the mask
            var img_mask = e.images[i];  // loaded images in an array

            w = img_mask.width;          // now you will have a valid
            h = img_mask.height;         //  dimension here

            console.log("w = ", img_mask.width, " h = ", img_mask.height);

            tempCanvas.width = w;
            tempCanvas.height = h;

             // Her lages maska
            tempContext.drawImage(img_mask,0,0);
            tempContext.globalCompositeOperation = 'source-in';
            tempContext.drawImage(img_bg,arr_x[i],arr_y[i]);
            myCanvas.width = w;
            myCanvas.height = h;

            // Draws tempCanvas on to myCanvas
            console.log("tempCanvas: ", tempCanvas, " img_mask.src = ", img_mask.src);
            context.drawImage(tempCanvas, 0, 0);
            arr_data[i] = myCanvas;
            sendData(arr_data[i], [i]);                
        }
    };
}

注意:加载图像会使您的代码本质上异步。如果代码在绘制完成后立即依赖于某些其他函数,则必须在循环结束后从内部函数中调用该函数。

希望这会有所帮助。如果碎片仍然没有正确显示,请设置上传到f.ex的图像的小提琴。 imgur.com所以我们可以深入研究这个问题。

希望这有帮助!