In putting together a small canvas app I've stumbled across a weird behavior that only seems to occur in the default browser in Android.
When drawing to a canvas that has the globalCompositeOperation set to 'destination-out' to act as the 'eraser' tool, Android browser sometimes acts as expected, sometimes does not update the pixels in the canvas at all.
the setup:
context.clearRect(0,0, canvas.width, canvas.height);
context.drawImage(img, 0, 0, canvas.width, canvas.height);
context.globalCompositeOperation = 'destination-out';
draw a circle to erase pixels from the canvas:
context.fillStyle = '#FFFFFF';
context.beginPath();
context.arc(x,y,25,0,TWO_PI,true);
context.fill();
context.closePath();
a small demo to illustrate the issue can be seen here:
http://gumbojuice.com/files/source-out/
and the javascript is here:
http://gumbojuice.com/files/source-out/js/main.js
this has been tested in multiple desktop and mobile browsers and behaves as expected. On Android native browser after refreshing the page sometimes it works, sometimes nothing happens.
I've seen other hacks that move the canvas by a pixel in order to force a redraw but this is not an ideal solution..
Thanks all.
I did something like this, which forces the detachment of the canvas:
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (isStockAndroid) {
canvas.style.display = "none";
canvas.offsetHeight;
canvas.style.display = "block";
}
That seems to be the most efficient as far as FPS is concerned. Otherwise it's the not-so-nice:
canvas.width = canvas.width;
...which seemed to also get it all working normally for me. Haven't tested to see if the first is essentially the same as the second and resets canvas settings, though, but it seems to be getting a higher frame rate? Anyway that definitely clears things. For the native detection stuff try here: How to detect only the native Android browser
Related
Seems like an update on google chrome messed up my canvas rendering.
I have a pretty simple code that renders an image and text on canvas:
var onDraw = function() {
context.clearRect(0, 0, 256, 256);
context.drawImage(image, 0, 0, 256, 256);
context.fillText('TEST', 0, 20);
requestAnimationFrame(onDraw);
};
This code is terribly flickers on Chrome: https://jsfiddle.net/gp9jxn6q/ (just move your mouse over the page).
There are only 2 ways I found to prevent this behavior:
Call context.clearRect() for the whole canvas. But in this case I can not redraw dirty rectangles only.
Set image-rendering: pixelated for the canvas. In this case all fonts look terrible.
What else can be done with this?
This is a bug that started to appear more frequently since Chromium version 83 and even more so since 85 (so in addition to Chrome this also effects Opera and the new Edge).
I filed an issue at Chromium a few months ago and they are currently working on a fix:
https://bugs.chromium.org/p/chromium/issues/detail?id=1092080
What happens is that the antialiasing is set to "nearest neighbour" in the next monitor frame after the drawImage() call. This can affect the source and destination element, and this affects any CanvasImageSource (image, video, canvas: https://developer.mozilla.org/en-US/docs/Web/API/CanvasImageSource).
It happens randomly because it is probably bound to the performance of the device and timing, because some graphics settings can fix the bug while at the same time they can create the bug on another device.
But I also have some good news. I think I've finally found a workaround you can execute after the drawImage() call that resets the antialiasing: http://jsfiddle.net/u7k5qz2p/
<div class="chromium-issue-1092080-workaround__wrapper">
<canvas id="canvas" width="300" height="300"></canvas>
<div class="chromium-issue-1092080-workaround__overlay"></div>
</div>
context.drawImage(image, 0, 0, 256, 256);
chromiumIssue1092080WorkaroundOverlay.style.transform = `scaleX(${Math.random()})`
What it does is overlay a div on top of the canvas. And after each drawImage() call changes the scaleX in the transform style to trigger a reset of the antialiasing setting in the canvas.
I built a webapp to capture an image using getUserMedia(),
but the quality of the image is not so good, it's a little blurry!. I tried to take the picture via my phone's camera and there is a big difference!
I like to know why taking the picture from browser is not as good as taking it from the phone's camera! and if it's possible, how can it be done to get the image captured via browser (getUserMedia()) with the best quality?
capture.addEventListener('click', () => {
c.style.display = "flex";
c.width = video.outerWidth();
c.height = video.outerHeight();
ctx.drawImage(video, 0, 0, c.width, c.height);
video.style.display = "none";
})
Thanks in advance.
Try using these lines
c.width = video.videoWidth;
c.height = video.videoHeight;
Then your canvas will have the same resolution as the captured video. You can use CSS to scale the canvas appropriately for display, and most browsers do a good job of making the scaled display look good.
As part of my studies i'm modifying some parts in an open-sourced "Pong" game I found online (created by Kushagra Agarwal).
Game's Fiddle
I'm trying to set a nice background image to the game,
that will not disturb the gameplay experience.
I've tried many ways but so far Iv'e only managed to change the background color into a different color, but not to an actual image :
// Function to paint canvas
function paintCanvas() {
ctx.fillStyle = "blue/black/whatever";
ctx.fillRect(0, 0, W, H);
Trying to work out ctx.fillStyle to display an image didn't went well since paintCanvas() is constantly looping, and if gets other value then a "color" string (for example a background image that weighs 500kb) it will create huge delays in the game.
Anyone has an idea?
Thank's,
Roy
I think it looks nice if your just put a background image on the body tag!
<body background="someImage">
</body>
I commented out your paintCanvas(), inserted a clear, and inserted a body tag!
http://jsfiddle.net/XLKFC/11/
// Function to paint canvas
function paintCanvas() {
canvas.width = canvas.width; // Will clear the canvas
//ctx.fillStyle = "blue";
//ctx.fillRect(0, 0, W, H);
}
I need a little help from you. I have created a canvas image using excanvas.js and it was rendered properly. Now i want to use that same canvas to draw another image but before that i need to clear that same canvas otherwise images will be overlapped over each other.
Can anyone please explain how can i call fillRect() or any another method from excanvas.js for the same canvas element ?? I am using IE 8 as browser.
ctx.clearRect(0, 0, excanvas.width, excanvas.height);
or perhaps:
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, excanvas.width, excanvas.height);
Where excanvas is your DOMElement canvas, and ctx being its 2d context (via getContext('2d')).
Right now I have one large canvas where everything is drawn. The canvas is created directly in the JS file:
var canvas = document.createElement ("canvas");
var ctx = canvas.getContext("2d");
canvas.width = document.width;
canvas.height = document.height;
document.body.appendChild(canvas);
It resizes itself to the browser window.
I have a gradient background being drawn onto the one canvas along with all the other elements. The colors for the background are randomly generated at each game mode change (eg when the main menu is toggled to the game, then the level end screen, etc). Currently I'm drawing these onto the canvas like this:
var grad1 = ctx.createRadialGradient(canvas.width / 2, canvas.height / 2, 0, canvas.width / 2, canvas.height / 2, 500);
grad1.addColorStop(0, bgGradStop1);
grad1.addColorStop(1, bgGradStop2);
ctx.fillStyle = grad1;
ctx.fillRect(0, 0, canvas.width, canvas.height);
I read numerous times that having a separate canvas for the background is better for performance. Is there anything specific that I need to do to make this effective or can I simply create another canvas and draw the background using the same exact code, only modified to use the other canvas, like this:
var grad1 = ctxBg.createRadialGradient(canvasBg.width / 2, canvasBg.height / 2, 0, canvasBg.width / 2, canvasBg.height / 2, 500);
grad1.addColorStop(0, bgGradStop1);
grad1.addColorStop(1, bgGradStop2);
ctxBg.fillStyle = grad1;
ctxBg.fillRect(0, 0, canvasBg.width, canvasBg.height);
Or does getting any performance benefit involve some totally different method of using the second canvas?
Also, if it really is just a matter of doing the exact same thing but on a different canvas, would there be any benefit to also for example moving the HUD text to its own canvas while other entities are moving? Even separating various types of entities onto their own canvases? How far does the benefit of multiple canvases actually stretch?
Or does getting any performance benefit involve some totally different method of using the second canvas?
You've got the right idea of how to do it, though I don't think a second canvas is necessary in your case.
The important thing from a performance perspective is that you don't want to have to keep constructing and filling the gradient. If all you're doing in your draw loop is:
ctx.fillStyle = grad1;
ctx.fillRect(0, 0, canvas.width, canvas.height);
Then you should be pretty swell here. I don't think having a second canvas in the background will help you much after that. There might be a slight performance boost, but who wants to have to keep track of additional DOM elements if you don't have to?
It's a little hard to test the performance of having a second canvas behind your main one, but instead of having two large canvases in the DOM one similar alternative is to draw the gradient an in-memory canvas and always call ctx.drawImage(inMemCan, 0, 0); instead of setting the gradient and drawing it. Drawing images is known to be fast, and using two canvases this way is probably close to the speed you could expect from two on-page canvases (and hopefully it would be faster).
So we could test drawing the gradient from an in-memory canvas to your main canvas versus drawing the plain old gradient. Here's a simple test with a large canvas:
http://jsperf.com/gradient-vs-immemcan
They're pretty equal it seems. If this one thing is the only thing in your background I wouldn't worry about it. There are probably bigger performance fish for you to fry first.
I'd save the benefit of multiple canvases for when a relatively complicated background updates rarely but independently of the foreground. If your background was instead made with 30 gradients and some paths, then using a second canvas (or an in-memory canvas to cache an image) would give you a sizable boost.
It resizes itself to the browser window.
Just a reminder that the full screen API works pretty well in webkit and firefox if you want to look into it in the future.