我正在尝试发布简单的拼图益智游戏的后端。游戏使用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 );
}
?>
答案 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所以我们可以深入研究这个问题。
希望这有帮助!