Simplest slideshow in HTML5 canvas, canvas.context.clearRect not working with setTimeout

13,869

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.

Share:
13,869
Boris Burkov
Author by

Boris Burkov

Updated on June 05, 2022

Comments

  • Boris Burkov
    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 the draw_next_image and only then execute ctx.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 that ctx.clearRect gets called right away and actually prior to draw_next_image. So, by the time Timeout expires and calls draw_next_image, execution of code may reach and arbitrary line of code. In my case all the 4 clearRect 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, without clearRects, which got executed long before.

  • Boris Burkov
    Boris Burkov almost 11 years
    Rob 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
    Boris Burkov almost 11 years
    Ah, 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
    Rob W almost 11 years
    Right. 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
    Boris Burkov almost 11 years
    Thanks! Hm, I wonder, is there no synchronous wait function in javascript at all?