【问题标题】:Making masks with Canvas globalCompositeOperation for jigsaw puzzle game backend使用 Canvas globalCompositeOperation 为拼图游戏后端制作面具
【发布时间】:2014-05-25 09:41:39
【问题描述】:

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

我正在制作的应用程序使用 ajax 将每个完成的部分发送到服务器端 .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 );
}
?>

【问题讨论】:

    标签: javascript php html canvas html5-canvas


    【解决方案1】:

    问题

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

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

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

    另一个问题是,在您的循环中,您将覆盖图像变量,因此在加载时只会使用最后一张图像。

    解决方案

    解决方案是在开始循环之前制作或使用图像加载器。制作一个很容易,但为简单起见,我将在此示例中使用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,这样我们就可以更深入地研究这个问题。

    希望这会有所帮助!

    【讨论】:

      猜你喜欢
      • 2014-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多