How to redraw canvas (every 250ms) without flickering between each redraw? - javascript

I wrote a function that draws out a slice of a pizza based on how big you specify it to be (in degrees)
function drawProgress(degs){
var canvas = document.getElementById('progress');
var context = canvas.getContext('2d');
context.globalAlpha=1;
var img = new Image();
img.onload = function(){
context.beginPath();
context.arc(canvas.width/2,canvas.height/2, canvas.height/2, 0, degs * (-Math.PI/180), true);
context.lineTo(canvas.width/2, canvas.height/2);
context.clip();
context.drawImage(img, 0, 0, canvas.width,canvas.width);
}
img.src = 'pizza.png';
}
When I try to call this function every 250ms, the progress is not updated after the first draw.
function runsEvery250ms(percent){
drawProgress(3.6 * percent);
}
What changes do I need to make to the code to get the canvas to redraw each time drawProgress(degs) is called? Is it possible to perform redraws without causing the pizza to flicker?

Use context.clearRect(0, 0, canvas.width, canvas.height); and cache your image, don't reload each time you refresh
UPDATE: No idea if this will work, untested and been a while since I used canvas but try it
var canvas = document.getElementById('progress');
var context = canvas.getContext('2d');
var img = new Image();
var imgLoaded = false;
img.src = 'pizza.png';
img.onload = function(){
imgLoaded = true;
}
function drawProgress(degs){
context.save();
context.clearRect(0, 0, canvas.width, canvas.height);
context.globalAlpha=1;
context.beginPath();
context.arc(canvas.width/2,canvas.height/2, canvas.height/2, 0, degs * (-Math.PI/180), true);
context.lineTo(canvas.width/2, canvas.height/2);
context.clip();
if (imgLoaded) context.drawImage(img, 0, 0, canvas.width,canvas.width);
context.restore();
}

Related

Converting canvas to PNG returns blank image

I am experiencing an issue with converting a canvas to PNG.
Although the canvas looks exactly as I want it and the conversion from canvas to data URL PNG seems right, the image is blank.
I also tried converting a div to PNG but it did not work for me because I wanted a greyscale filter to be applied. Anyone have any ideas?
JavaScript
var imgis = new Image();
var bubble = new Image();
var canvasWidth;
var canvasHeight;
var ctx = canvas.getContext('2d');
bubble.onload = function() {
var imgis = new Image();
var bubble = new Image();
var ctx = canvas.getContext('2d');
bubble.onload = function() {
// set the canvas' size
canvas.width = this.width;
canvas.height = this.height;
// first fill a rect
ctx.fillStyle = 'rgba(255, 255, 255, 0)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// set the gCO
ctx.globalCompositeOperation = 'luminosity';
// if the browser doesn't support Blend Modes
console.log(ctx.globalCompositeOperation)
if (ctx.globalCompositeOperation !== 'luminosity')
fallback(this);
else {
// draw the image
ctx.drawImage(this, 0, 0);
ctx.drawImage(imgis, 30, 60);
// reset the gCO
ctx.globalCompositeOperation = 'source-over';
}
}
imgis.crossOrigin = "anonymous";
bubble.crossOrigin = "anonymous";
imgis.src = "image1 src";
bubble.src = "image2 src";
function fallback(img) {
// first remove our black rectangle
ctx.clearRect(0, 0, canvas.width, canvas.height);
//draw the image
ctx.drawImage(img, 0, 0);
ctx.drawImage(imgis, 30, 60);
// get the image data
var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var d = imgData.data;
// loop through all pixels
// each pixel is decomposed in its 4 rgba values
for (var i = 0; i < d.length; i += 4) {
// get the medium of the 3 first values
var med = (d[i] + d[i + 1] + d[i + 2]) / 3;
// set it to each value
d[i] = d[i + 1] = d[i + 2] = med;
}
// redraw the new computed image
ctx.putImageData(imgData, 0, 0);
}
canvas = document.getElementById('canvas');
var image = Canvas2Image.convertToPNG(canvas);
console.log(image.src);
// document.getElementById('theDemo').src = image.src;
var image_data = $(image).attr('src');
console.log(image_data);
$("#theDemo").attr('src', image_data);
HTML
<canvas id='canvas' > </canvas>
<img src="" id="theDemo" />
I assume you're using canvas2image. You should replace var image = Canvas2Image.convertToPNG(canvas); with Canvas2Image.convertToPNG(canvas, width, height). Hopefully that helps!
EDIT Since the issue is with the actual canvas to base64 conversion, you can try to use the .toDataURL() method instead of using that library. My comment explains how to test this in your specific code.

How would I generate a pulsating effect on an image that is to be drawn inside of a canvas?

var testPhoto = new Image();
testPhoto.src = "http://plan9.bell-labs.com/plan9/img/9logo.jpg"
testPhoto.className = "pulse";
and then:
topSlice.drawImage(testPhoto, 100, 200, 40, 40);
It appears the pulsating effect doesn't work after drawing the image, how should I fix this? I'm following this tutorial for the pulsating effect.
You can use Window.requestAnimationFrame instead and work with blend modes / alpha blending:
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
image = new Image();
image.src = "http://plan9.bell-labs.com/plan9/img/9logo.jpg";
function update(timestamp) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.globalAlpha = Math.sin(timestamp/100) * 0.5 + 0.5;
context.drawImage(image, 0, 0);
window.requestAnimationFrame(update);
}
window.requestAnimationFrame(update);
<canvas id="canvas"></canvas>

Image isn't drawn after canvas is cleared (html5 canvas)

I am trying to make a variant of the boomshine game in javascript and everything works when I draw a circular shape with the arc function. However when i try to replace the arc function with the drawImage function to use a coin image instead of a circular shape I start having problems when I clear the canvas to delete the previous drawn circular shapes. If I don't clear the canvas before rendering the images, the images are drawn on the canvas except the old images are still on the canvas. But when i do clear the canvas before rendering the images again, nothing is drawn on the canvas.
I have included screenshots, the links are below.
This is how I clear the canvas:
var ctx = game.context;
ctx.fillStyle = "darkgray";
ctx.fillRect(0, 0, game.canvas.width, game.canvas.height);
This is how i draw the image:
function drawImageBall(x,y,radius,startAngle,color)
{
var img = document.createElement('img');
img.src = 'img/coin-icon.png';
var tmpCtx= game.context;
var ax = x-radius;
var ay = y-radius;
img.onload = function() {
tmpCtx.save();
tmpCtx.beginPath();
tmpCtx.arc(x, y, radius, 0, Math.PI * 2, true);
tmpCtx.closePath();
tmpCtx.clip();
tmpCtx.drawImage(img, ax, ay, img.width, img.height);
tmpCtx.beginPath();
tmpCtx.arc(0, 0, radius, 0, Math.PI * 2, true);
tmpCtx.clip();
tmpCtx.closePath();
tmpCtx.restore();
};
}
Clearing canvas (screenshot)
Without clearing canvas (screenshot)
Keep in mind that downloading the img will take some time.
During that downloading time, Javascript does not stop(!). Instead JS will continue executing any following code. This is causing your unexpected problems.
So download the img just once at the start of your app. That way your drawImage will be done in the order that you expect because there will be no delay while your image is downloading.
Using your code, I maked some changes, I removed the tmpTcx.clip(), look the fidlle. Tip: For performace questions you don't need load the image every time that you want write the canvas.
Poor Example: https://jsfiddle.net/wf4z0d2h/1/
function clearCanvas(){
var ctx = game.context;
ctx.fillStyle = "darkgray";
ctx.fillRect(0, 0, game.canvas.width, game.canvas.height);
}
function drawImageBall(x,y,radius,startAngle,color)
{
if(x == undefined){x = 100;}
if(y == undefined){y = 100;}
if(radius == undefined){radius = 40;}
//var img = document.createElement('img');
//img.src = 'img/coin-icon.png';
//img.src = "http://ps2.lansa.com/images/icons/normal/256/coin_256.png";
var tmpCtx= game.context;
var ax = x-radius;
var ay = y-radius;
//img.onload = function() {
tmpCtx.save();
tmpCtx.beginPath();
tmpCtx.arc(x, y, radius, 0, Math.PI * 2, true);
tmpCtx.stroke(); // Draw it
tmpCtx.closePath();
//tmpCtx.clip();
tmpCtx.drawImage(img, ax, ay, img.width, img.height);
//tmpCtx.beginPath();
//tmpCtx.arc(0, 0, radius, 0, Math.PI * 2, true);
////tmpCtx.clip();
//tmpCtx.stroke(); // Draw it
//tmpCtx.closePath();
//tmpCtx.restore();
//};
}
var img = document.createElement('img');
//img.src = 'img/coin-icon.png';
img.src = "http://ps2.lansa.com/images/icons/normal/256/coin_256.png";
//drawImageBall();
img.onload = function(){
x = 0;
y = 0;
setInterval(function(){
x = x+10;
y = y+10;
clearCanvas();
drawImageBall(x,y);
},300);
}

Redraw canvas on mouseover

I already opened a question about this canvas I'm creating here but with a different issue at: Make rectangle overlay image with canvas
I'd like to know if it's possible to animate the radius of an arch on mouseover?
Here's what I've: jsfiddle
// Options
var maxImageWidth = 250,
maxImageHeight = 196,
radius = 50;
var canvas = $('#ddayCanvas'),
canvasWidth = canvas.width(),
canvasHeight = canvas.height(),
sectorColor = $('.product-box').css('background-color'),
context = canvas[0].getContext('2d'),
imageSrc = canvas.data('image');
function drawDday (option) {
context.clearRect(0, 0, canvasWidth, canvasHeight);
if (typeof option != 'undefined'){
radius = option;
}
var imageObj = new Image();
imageObj.onload = function() {
var imageWidth = imageObj.width,
imageHeight = imageObj.height;
if (imageWidth > maxImageWidth){
imageHeight = imageHeight - (imageWidth - maxImageWidth);
imageWidth = maxImageWidth;
}
if (imageHeight > maxImageHeight) {
imageWidth = imageWidth - (imageHeight - maxImageHeight);
imageHeight = maxImageHeight;
}
context.drawImage(imageObj, Math.ceil((canvasWidth - imageWidth) / 2), Math.ceil((canvasHeight - imageHeight) / 2), imageWidth, imageHeight);
// Why does this rectangle not overlay the previous image?
context.fillStyle = sectorColor;
context.rect(0, 0, canvasWidth, canvasHeight);
context.arc(canvasWidth/2, canvasHeight/2, radius, 0, Math.PI*2, true);
context.fill();
};
imageObj.src = imageSrc;
}
drawDday();
canvas.hover(function(){
drawDday(90);
}, function(){
drawDday(20);
});
I've tried to call the function with a parameter for the radius on hover, and "overwrite" the canvas with clearRect. But unfortunately it only gives me a flickr effect...
EDIT
I just realized that the mouseover/out works when the initial radius is bigger then the hover radius. Very strange...
You are reloading the image with each drawDday call. This will cause the flicker effect. Try loading the image once and reusing the imageObj reference to draw to the canvas.
imageObj is loaded once and reused for each drawDday call. See: http://jsfiddle.net/Vr5k9/4/
function drawDday (radius) {
context.clearRect(0, 0, canvasWidth, canvasHeight);
context.drawImage(imageObj, Math.ceil((canvasWidth - imageWidth) / 2), Math.ceil((canvasHeight - imageHeight) / 2), imageWidth, imageHeight);
context.fillStyle = sectorColor;
context.beginPath();
context.rect(0, 0, canvasWidth, canvasHeight);
context.arc(canvasWidth/2, canvasHeight/2, radius, 0, Math.PI*2, true);
context.closePath();
context.fill();
}
Edit: Please note the context.beginPath() and context.closePath(). This lets the canvas sub-system know that each time the function is called it's a new path. Otherwise the new path will combine with the old one.
Edit: With simple animation effect on mouse move: http://jsfiddle.net/CvuyN/2/

Why does the first load fail, but subsequent loads work?

The question is best demonstrated with a jsFiddle. Here is is.
So according to this code:
var canvas = document.getElementById('canvas');
canvas.width = 200;
canvas.height = 150;
var context = canvas.getContext('2d');
context.rect(20,20,50,50);
context.stroke();
var image = new Image();
image.src = canvas.toDataURL("image/png");
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(image, 0, 0);
you should see a rectangle drawn to the canvas every time you load the page. However, for some reason the first page load always shows nothing, a "blank"...
This point can be proven by opening the jsFiddle link in separate Chrome "Incognito" tabs. You won't ever see the rectangle load on the first time. Refreshing however it will appear.
And just to prove that you are indeed supposed to be seeing a rectangle on the initial load, try updating the jsFiddle to this:
var canvas = document.getElementById('canvas');
canvas.width = 200;
canvas.height = 150;
var context = canvas.getContext('2d');
context.rect(20,20,50,50);
context.stroke();
//var image = new Image();
//image.src = canvas.toDataURL("image/png");
//context.clearRect(0, 0, canvas.width, canvas.height);
//context.drawImage(image, 0, 0);
and you will see the rectangle on each first-load.
Why does the first load show nothing?
You have to wait for the image to load before you can dray it, and it works on the reload because it gets cached
image.onload = function(){
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(image, 0, 0);
}
http://jsfiddle.net/hJ9WQ/1/

Categories

Resources