I'm trying to build a canvas on a page which updates itself whenever a user draws something on it. However it looks as if the following piece of code ( from index.html) is causing an error
socket.on("drawn_complete",function(data){
ctx.putImageData(data.image,0,0);
console.log("Everthing worked technically");
});
Here is the error that gets outputted in my console:
Uncaught TypeError: Failed to execute 'putImageData' on 'CanvasRenderingContext2D': No function was found that matched the signature provided
my data.image is what gets returned from the following code:
ctx.getImageData(0,0,200,100); // ctx is canvas.getContext("2d")
Here is my canvas in my html:
<canvas id="myCanvas" width="200" height="100"
style="border:1px solid #000000;">
</canvas>
Here is the full nodeJS:
var http = require("http")
var server = http.createServer(handler).listen(1337);
console.log("Server created at 127.0.0.1:1337");
var io = require("socket.io").listen(server);
function handler(req,res)
{
console.log("Client Connected");
res.writeHead(200,{"Content-type": "text/plain"});
res.end("Hello World");
}
io.sockets.on("connection",function(socket){
socket.on("drawing",function(data){
var image = data["image"];
io.sockets.emit("drawn_complete",image);
});
});
Drawing is emmited when the user holds and moves the mouse inside my canvas:
Here is the code:
c.onmousedown = function(evt){
moving = true
};
c.onmousemove = function(evt){
if(moving == true)
{
console.log("holding and moving");
var x = evt.clientX - rect.left;
var y = evt.clientY - rect.top;
console.log("X: " + x + " Y: " + y);
ctx.fillRect(x,y,1,1);
socket.emit("drawing",{"image": ctx.getImageData(0,0,200,100)});
}
};
c.onmouseup = function(evt){
moving = false;
};
UPDATE
Here is the enitre script:
<script>
window.onload = function(){
var socket = io.connect("http://localhost:1337");
socket.on("drawn_complete",function(data){
ctx.putImageData(data.image,0,0);
console.log("Everthing worked technically");
});
var c = document.getElementById("myCanvas");
var moving = false;
console.log(c);
var ctx = c.getContext("2d");
var rect = c.getBoundingClientRect();
c.onmousedown = function(evt){
moving = true
};
c.onmousemove = function(evt){
if(moving == true)
{
console.log("holding and moving");
var x = evt.clientX - rect.left;
var y = evt.clientY - rect.top;
console.log("X: " + x + " Y: " + y);
ctx.fillRect(x,y,1,1);
socket.emit("drawing",{"image": ctx.getImageData(0,0,200,100)});
}
};
c.onmouseup = function(evt){
moving = false;
};
};
</script>
UPDATE As Palanik suggested I have trieed to serialize the image before emitting it:
var stringfy = JSON.stringify(ctx.getImageData(0,0,200,100));
socket.emit("drawing",{"image": stringfy});
It looks as if the console does still throw the same error
UPDATE ( 2 ) As @Palanik suggested I tried to serialise the object. I know the following code is not exactly what he recommended however I just want to highlight something very peculiar:
Sender:
var stringfy = ctx.getImageData(0,0,200,100);
console.log(stringfy)
var sent = JSON.stringify(stringfy);
socket.emit("drawing",{"image": sent});
Reciever
socket.on("drawn_complete",function(data){
imageData = JSON.parse(data.image);
console.log(imageData);
ctx.putImageData(imageData,0,0);
console.log("Everthing worked technically");
});
The interesting bit here is that the console.log of imageData prints out an oject with the same exact properties that ImageData has. Height, Width and raw. Take a look here:
{"height":100,"width":200,"data":{"0":0,"1":0,"2":0,"3":0,"4":0,"5 etc... }
So as I can see from here I am able to get the object back. If I was in java a cast would be enough;
ImageData object = (ImageData) imageData // Is there a way to do this?
ImageData may not be portable. Try serializing imagedata before sending and deserialize and create imagedata on the receiving end.
=== Update ===
Something like :
canvasData = ctx.getImageData(0,0,200,100)}
myData = {
height : canvasData.height,
width : canvasData.width,
raw : canvasData.data
};
socket.emit("drawing",{"image": myData});
imgData = ctx.getImageData(myData.width, myData.height);
for (var i = 0; i < myData.raw.length) {
imgData.data[i] = myData.raw[i];
}
ctx.putImageData(imgData, 0, 0);
This is untested code. Just an idea for you to try it out.
The solution here is to use canvas.toDataURL();
.
This will return a Base64-encoded image-string (with MIME-Type and encoding in the head).
var canvas = getMyCanvas(),
ctx = canvas.getContext("2d"),
encodedImageData = canvas.toDataURL(),
image = new Image();
image.src = encodedImageData;
ctx.drawImage(image, x, y, w, h...);
You can pass these encoded strings to and from the server, happily.
You can use them in regular HTML <img src="data:image/png;base64,...">
, as well.
Also, you can pass toDataURL
a couple of arguments:
canvas.toDataURL("image/jpeg", 0.5);`
Where, the first argument is the requested file type, and the second is the requested image-quality level (between 0 and 1).
There is no guarantee that any browser is going to support what you asked for, though, and you might just get a .png file back, anyway (which is likely what you'd want for a game, for transparencies, regardless).
Also, the Android 2.2 browser and lower, and IE9, and I think Mobile Safari 5.1 (original iPads), don't support .toDataURL
.
I might be wrong about the iPads, but they don't support other HTML5 APIs which are useful in conjunction.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With