I have a task to create social network
I have a home page that displays all drawings created by users via canvas. Users can draw on their own drawing and can add contributors which have righ to draw on their drawing also. The thing is they can both draw in the same time somehow.
The thing i want to do is, that when one user draws the canvas image, it is updating for other user who is watching that same drawing without refreshing the page.
I want to note that program works well, the only problem is that you have to refresh the page to see changes of drawing
Here is my client side code where i add previously drawed picture to empty canvas before new drawing
canvas = document.getElementById('can');
contain = document.getElementById('contain');
ctx = canvas.getContext("2d");
var imageObj = new Image();
imageObj.src = ''+drugi+'/'+prvi+'.png';
imageObj.onload = function() {
ctx.drawImage(this, 0, 0);
};
Here is the request to ajax to save drawing after every start of the draw() function (which is everytime you move your mouse while drawing on canvas)
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(currX, currY);
ctx.strokeStyle = x;
ctx.lineWidth = y;
ctx.stroke();
ctx.closePath();
var dataURL = canvas.toDataURL("image/png");
var ajax = new XMLHttpRequest();
ajax.open("POST",'saveimg.php',false);
ajax.setRequestHeader('Content-Type', 'application/upload');
ajax.send(dataURL );
This is server side code
<?php
session_start();
$crtez = $_SESSION['tmpdrawing'];
$kategorija = $_SESSION['tmpkat'];
if (isset($GLOBALS["HTTP_RAW_POST_DATA"]))
{
$imageData=$GLOBALS['HTTP_RAW_POST_DATA'];
$filteredData=substr($imageData, strpos($imageData, ",")+1);
$unencodedData=base64_decode($filteredData);
$fp = fopen($kategorija . '/' . $crtez . '.png', 'wb' );
fwrite( $fp, $unencodedData);
fclose( $fp );
?>
Now when we have a code, actual thing i want is the first code, which changes canvas appearance to be launched for example in every 3 seconds, so the other users currently on that drawing can see the picture updating without refreshing page.
If you need more code in client side i will post.
I want to say again that the program works perfectyl, the only thing i need to finish it is auto updating canvas via ajax
First you should wrap javascript code into function and than just call that function when something is changed on the server side. As I know, there are two concepts for communitaion between clients and server:
Server Pooling - using javascript, client sends a HTTP request every X seconds and do something when server send some updates, for example update canvas. This is an "old school" and performance-poor pattern, maybe you should avoid it. If you like The Simpsons, this is it: Are we there yet Take a look: Server Pooling Example
Web Socket Communication - full duplex communication between server and clients. When something happened on the server side, clients are informed almost immediately. I had a lot of problems to implement web socket communication with PHP and Apache Server last year when I worked with that kind of project and don't know if anything is better right now. But, after reading some books for realtime applications development, I made it with Pusher, that's an commercial service but also have free plans.
Related
I generate array of pixels in client side JavaScript code and convert it to a blob. Then I pass URL of the blob as image.src and revoke it at image.onload callback. I don't keep any references to the data, generated by the previous steps, so this data may be freed by GC.
There are many images generated this way, and it works fine. But sometimes user may want to save the generated image by clicking on a Save button near the image. I don't want to generate image again, because generation is slow, and the image is already generated and is visible on screen. So I want to get my pixels back from the image. I tried to create canvas again, draw image on it and then call toBlob, but browser treats this image as cross origin and throws an exception: "Failed to execute 'toBlob' on 'HTMLCanvasElement': tainted canvases may not be exported". Similar errors I get with canvas.toDataUrl and canvasContext.getImageData.
Is there a workaround for this problem?
I also tried to create canvases instead of images, but when I create the second canvas, the content of the first one clears.
Added
This error occurs only in the Chrome and other WebKit browsers. Firefox and MS Edge work fine. And when I commented out line of code that revoked blob url, this error disappeared - I can draw the image on the canvas and get its pixel data without CORS issues. But it is pointless to do so, because I already have blob that is not deleted.
But my page may generate many images - it depends on its user and is unlimited. Size of images is also unlimited - it may be useful to generate even 4096x4096 images. So I want to reduce memory consumption of my page as much as possible. And all these images should be downloadable. Generation of most images uses previously generated images, so to regenerate last image in a chain, I must to regenerate all images.
So I need a workaround only for Chrome browser.
Added 2
I tried to reproduce this problem in JS Fiddle, but couldn't. However locally my code doesn't work - I developed my app locally and I haven't tried running it on server. Create test.html file on your computer and open it in browser (locally, without server):
<html>
<body>
<pre id="log"></pre>
</body>
<script>
var log = document.getElementById("log");
var canvas = document.createElement("canvas");
canvas.width = canvas.height = 256;
var ctx = canvas.getContext("2d");
canvas.toBlob(function(blob) {
var img = new Image();
var blobUrl = URL.createObjectURL(blob);
img.src = blobUrl;
img.onload = function()
{
URL.revokeObjectURL(blobUrl);
ctx.drawImage(img, 0, 0);
try { canvas.toBlob(function(blob) { log.textContent += 'success\n'; }); }
catch(e) {log.textContent += e.message + '\n';}
};
});
</script>
</html>
It will print Failed to execute 'toBlob' on 'HTMLCanvasElement': Tainted canvases may not be exported..
So, I think, my workaround is to detect that page is run on combination of WebKit browser and file:/// protocol. And I can to defer revoking blob URL until page unload only for this combination.
That indeed sounds like a bug in chrome's implementation, you may want to report it.
What seems to happen is that chrome only checks if the image has been loaded through a clean origin when it is drawn for the first time on the canvas.
Since at this time, you already did revoke the blobURI, it can't map this URI to a clean origin (file:// protocol is quite strict on chrome).
But there seems to be a simple workaround:
By drawing this image on a [the reviver]* canvas before revoking the blobURI, chrome will mark the image as clean, and will remember it.
*[edit] Actually it seems it needs to be the same canvas...
So you can simply create your export canvas before-hand, and draw each images on it (to save memory you can even set it to 1x1px when you don't use it for the export) before you do revoke the image's src:
// somewhere accessible to the export logic
var reviver = document.createElement('canvas').getContext('2d');
// in your canvas to img logic
canvas.toBlob(function(blob){
var img = new Image();
img.onload = function(){
reviver.canvas.width = reviver.canvas.height = 1; // save memory
reviver.drawImage(this, 0,0); // mark our image as origin clean
URL.revokeObjectURL(this.src);
}
img.src = URL.createObjectURL(blob);
probablySaveInAnArray(img);
};
// and when you want to save your images to disk
function reviveBlob(img){
reviver.canvas.width = img.width;
reviver.canvas.height = img.height;
reviver.drawImage(img,0,0);
reviver.canvas.toBlob(...
}
But note that this method will create a new Blob, not retrieve the previous one, which has probably been Collected by the GarbageCollector at this time. But it is unfortunately your only way since you did revoke the blobURI...
I'm currently working on an online pictionnary (one person do a drawing and the others have to guess what it is).
Currently i'm sending my entire image from the drawer (in a canvas) base64 on the "mouseup" DOM event to the server and i send it back to the guesser in the same format, my problem is that it seems pretty heavy and i have no idea how to have a lighter payload.
$('#canvas').on('mouseup touchend', function() {
mouse.down = false;
// My problem is sending canvas.toDataURL() every time
update_canvas(canvas.toDataURL());
});
// I use this function inside ActionCable so this.perform will send the data to my channel
var update_canvas = function(data_url) {
this.perform('update_canvas', {data_url: data_url});
},
Do you have a better way to send my image data ?
You could either send a list of mouse positions or incremental changes of the canvas, i.e send only new drawings using a second canvas.
Well i think the best approach would be to send the coordinates you are drawing, i have implemented a similar kind of thing, u cn hv a look
http://connectboard.herokuapp.com/
open it in two or more different browser or computer and draw.
I created two html clients that communicate with each other using a websocket server. One client draw a 3D model on it's canvas using Three.js and sends the canvas context, which is webGl, as binary data to the other client thought the websocket server.
The problem is that the readPixels() method is too slow. I need to make this stream as fluid as possible.
function animate() {
requestAnimationFrame( animate );
render();
var ctx = renderer.getContext();
var byteArray = new Uint8Array(1280 * 720 * 4);
ctx.readPixels(0,0,1280, 720, ctx.RGBA, ctx.UNSIGNED_BYTE, byteArray);
socket.send(byteArray.buffer);
}
renderer is a THREE.WebGLRenderer.
Any suggestions?
EDIT:
Here is the code that I used as basis for the 3D drawing link
You could try using canvas.toDataURL("image/jpg"); (or png) to grab the imagedata, that's a reliable way of getting the image data from a WebGL context. I'm not so sure how performance compares to readPixels, I've only ever used it for screenshots.
The application aim to transfer drawings from client canvas to the admin canvas using web sockets the most smoothly possible. At every mousemove event, the client canvas is compressed to png and sent to server through websocket to finally arrive in the admin window where the image is drawn into the admin canvas.
Actually, the code lag a little in the admin window. It seems the bottleneck is ctx.drawImage() but I am not sure.
I wonder if there is a way to first find the bottleneck and then a way to optimise the transfer with debounce or web workers or other.
Client side:
<canvas id="clientCanvas" style="position: absolute; top: 0px; left: 0px;" width="1563" height="528">
function onMouseMove(e) {
var canvas = document.getElementById('clientCanvas');
var ctx = canvas.getContext("2d");
var imageData = canvas.toDataURL("image/png");
socket.emit('SS_onMouseMove', {imageData: imageData});
};
admin side:
<canvas id="adminCanvas" style="position: absolute; top: 0px; left: 0px;" width="1563" height="528">
var canvas = document.getElementById("adminCanvas");
var ctx = canvas.getContext("2d");
var image = new Image();
image.onload = function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(image, 0, 0);
};
socket.on("SS_onMouseMove", function(response) { onClientMouseMove(response); });
function onClientMouseMove(response) {
image.src = response.imageData;
}
The problem is you are passing the entire image multiple times a second which is both a heavy load on the client generating the image and the connection which is passing around 2 mb (image as dataUrl) 30 to 40 times a second.
The solution would be to:
When the connection is made send the original image once
When mouse events occur on the client, send only those mouse events to the admin and use the same functionality used on the client to render the changes from the mouse event on the admin.
This will decrease the load on the connection dramatically and will require less processing on the client since it wont have to generate a png -> dataUrl multiple times a second
I'm trying to run an html5 canvas script on a node server. The ideas is to have a client page show a canvas with some simple animation/graphics and then send this image data (info about each pixel) to the server.
function draw() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var socket = io.connect();
canvas.addEventListener('mousemove', function(evt)
{
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(evt.clientX, evt.clientY);
ctx.stroke();
pixels = ctx.getImageData(0, 0, 400, 400);
socket.emit('message', pixels);
});
}
However, socket.emit causes the script (the animation in the canvas) to run too slow. The script is supposed to display a line from the origin (top left) to current mouse position. The lines are appearing but irregularly - a bunch of lines every 2 or 3 seconds. If I comment out the socket.emit, the sketch runs smoothly. This means that ctx.getImageData is not the one causing the delay.
Is there a way to send this information to the server without slowing down the script?
The pixel information sent to the server will later be used by a second client to populate its canvas. I'm trying to build something like a set of synchronized sketch - draw in one canvas and see the results in many. I'm really new to developing for the web so I'm not sure if this can even be done in real time.