Simplest slideshow in HTML5 canvas, canvas.context.clearRect not working with setTimeout
Solution 1
You have a few issues with that, including naming your image variables wrong.
Try this:
// 4 images
var image0 = new Image();
image0.src = "http://placekitten.com/200/300";
var image1 = new Image();
image1.src = "http://placekitten.com/205/305";
var image2 = new Image();
image2.src = "http://placekitten.com/300/400";
var image3 = new Image();
image3.src = "http://placekitten.com/800/600";
// array of 4 images
images = new Array(image0, image1, image2, image3);
// global counter and canvas
var counter = 0, ctx, interval;
// this is the main function
function draw(){
myCanvas = document.getElementById('myCanvas');
ctx = myCanvas.getContext('2d');
interval = setInterval(draw_next_image, 1000);
}
// this is the function called after each timeout to draw next image
function draw_next_image(){
ctx.clearRect(0, 0, myCanvas.width, myCanvas.height);
ctx.drawImage(images[counter], 0, 0);
counter++;
if (counter==images.length) {counter=0;}
}
window.onload = draw;
See: http://jsfiddle.net/8c9MM/1/ for example. You can also pause the slideshow since we're assigning interval to the setInterval()
function
Solution 2
How about this.
HTML
<!DOCTYPE HTML>
<html>
<head>
<title>Slider</title>
<meta charset="utf-8">
<link type="text/css" rel="stylesheet" href="css/main.css">
</head>
<body>
<canvas id="canvas" width="600" height="400"></canvas>
<script type="text/javascript" src="js/slider.js"></script>
</body>
</html>
Javascript
// The 3 images
var im1 = new Image();
im1.src = "img/kitten1.jpg";
var im2 = new Image();
im2.src = "img/kitten2.jpg";
var im3 = new Image();
im3.src = "img/kitten3.jpg";
// Starting position of the 3 images
var x1 = 0;
var x2 = -600;
var x3 = -1200;
var counter = 0;
var img_count = 0;
// Canvas
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
//This draws the first image when the page is loaded
ctx.drawImage(im1, x1, 0);
function sliderMove() {
if(counter <= 590) {
x1+=10;
ctx.drawImage(im1,x1,0);
x2+=10;
ctx.drawImage(im2,x2,0);
x3+=10;
ctx.drawImage(im3,x3,0);
counter+=10;
}
else {
counter = 0;
img_count++
if(img_count == 1) {
x1 = -1200;
}else if(img_count == 2) {
x2 = -1200;
}else if(img_count == 3) {
x3 = -1200;
}
// This stops move_loop once counter gets to 600
clearInterval(move_loop);
}
}
function moveImg() {
//This part moves all of the images 20px to the right every 10ms
move_loop = setInterval(sliderMove, 10);
if(img_count > 2) {
img_count = 0;
}
}
// This executes the moveImg function every 5 seconds
image_loop = setInterval(timer, 5000);
Sorry if the codes not very organized. This is the first time that I have created anything with javascript.
Boris Burkov
Updated on June 05, 2022Comments
-
Boris Burkov almost 2 years
Here is a code of a very simple slideshow, that should show 4 images in 4 seconds, one image per second. Instead, I get a 4-second delay and then all the images get drawn on top of each other. What am I doing wrong?
<html> <head> <script langugage="javascript"> // 4 images var image0 = new Image(); image0.src = "img/image0.png"; var image1 = new Image(); image1.src = "img/image1.png"; var image0 = new Image(); image2.src = "img/image2.png"; var image3 = new Image(); image3.src = "img/image3.png"; // array of 4 images images = new Array(image0, image1, image2, image3); // this is the main function function draw(){ myCanvas = document.getElementById('myCanvas'); ctx = myCanvas.getContext('2d'); counter=0; // this is the index of the next image to be shown for (var i=0;i<images.length;i++){ setTimeout(draw_next_image, 1000); ctx.clearRect(0, 0, myCanvas.width, myCanvas.height) } } // this is the function called after each timeout to draw next image function draw_next_image(){ ctx.drawImage(images[counter], 0, 0); counter++; if (counter>images.length) {counter=0;} } window.onload = draw; </script> </head> <body> <canvas id="myCanvas" width="800" height="600"></canvas> </body> </html>
UPDATE: THE ANSWER IS:
In the code above I mistakingly assumed that
getTimeout
function is synchronous, i.e. I expected, that upon its call the program execution is going to stop, wait 1000 milliseconds, then call thedraw_next_image
and only then executectx.clearRect
.In reality Javascript doesn't work like that. In fact
getTimeout
is asynchronous, i.e.getTimeout
sets a Timeout and returns almost instantly and the code execution continues, so thatctx.clearRect
gets called right away and actually prior todraw_next_image
. So, by the time Timeout expires and callsdraw_next_image
, execution of code may reach and arbitrary line of code. In my case all the 4clearRect
are going to be called almost at the same time, long before expiration of Timeouts. Then 1000 milliseconds later, all the 4 timeouts are going to expire almost immediately one after another, and all the 4 images going to be drawn almost at the same time, too, withoutclearRects
, which got executed long before. -
Boris Burkov almost 11 yearsRob W: thanks for answering. So, you've replaced setTimeout with setInterval to create a persistent loop. But I really want to create Timeouts one at a time, not one interval for all. I can't understand, why wouldn't that work?
-
Boris Burkov almost 11 yearsAh, I think, I got it. I expected setTimeout to be synchronous, but it is asynchronous: that means, it returns instantly, when gets called and next statement is executed right away (which is clearRect). That way all the 4 iterative clearRect's run almost instantly and only then the timouts expire and my 4
draw_next_image
functions get executed and all the 4 images are drawn on top of each other, right? -
Rob W almost 11 yearsRight. If you use setTimeout, you'd need to have 4 of them... so for each iteration you had, try doing
setTimeout(function, 1000 * iteration)
. This will make them in an synchronous-like way. -
Boris Burkov almost 11 yearsThanks! Hm, I wonder, is there no synchronous wait function in javascript at all?