简体   繁体   English

HTML5 canvas中最简单的幻灯片显示canvas.context.clearRect无法与setTimeout一起使用

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

Here is a code of a very simple slideshow, that should show 4 images in 4 seconds, one image per second. 这是一个非常简单的幻灯片演示的代码,该演示应在4秒钟内显示4张图像,每秒显示一张图像。 Instead, I get a 4-second delay and then all the images get drawn on top of each other. 取而代之的是,我得到了4秒的延迟,然后所有图像相互叠加绘制。 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, ie 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 . 在上面的代码中,我错误地假设getTimeout函数是同步的,也就是说,我期望在调用该函数时,程序执行将停止,等待1000毫秒,然后调用draw_next_image ,然后才执行ctx.clearRect

In reality Javascript doesn't work like that. 实际上,JavaScript不能那样工作。 In fact getTimeout is asynchronous, ie 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 . 实际上, getTimeout是异步的,即getTimeout设置Timeout并几乎立即返回,并且代码执行继续,因此ctx.clearRect被调用,并且实际上 draw_next_image 之前draw_next_image So, by the time Timeout expires and calls draw_next_image , execution of code may reach and arbitrary line of code. 因此,在Timeout到期并调用draw_next_image ,代码的执行可能会到达任意代码行。 In my case all the 4 clearRect are going to be called almost at the same time, long before expiration of Timeouts. 在我的情况下,所有4个clearRect几乎都将在超时到期之前几乎同时被调用。 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. 然后1000毫秒后,所有4个超时将几乎立即一个接一个地到期,并且所有4个图像也几乎都将在同一时间绘制,而没有clearRects ,该对象早已执行。

The problem is that in your code you are treating asynchronous functions as if they where synchronous. 问题是在代码中,您将异步函数视为同步函数。

The main points are here: 要点如下:

image0.src = "img/image0.png";
image1.src = "img/image1.png";
image2.src = "img/image2.png";
image3.src = "img/image3.png";
...
setTimeout(draw_next, delayInMilliseconds);

As these calls just falls through once they are invoked, and your code starts to execute the next step before the result from these are (possibly) ready. 由于这些调用一旦被调用就会掉线,并且您的代码将开始执行下一步,然后(可能)准备好结果。

You therefor need to chain your calls based on events, for example: 因此,您需要根据事件链接呼叫,例如:

//image counter as there is no guarantee that the last images loaded
//is the last one to finish
var loaded = 0, numOfImages = 4;

//first part of chain, invoke async load
var image0 = document.createElement('img'); //this will work in new Chrome
var image1 = document.createElement('img'); //instead of new Image
var image2 = document.createElement('img');
var image3 = document.createElement('img');

//common event handler when images has loaded with counter
//to know that all images has loaded
image0.onload = image1.onload = 
image2.onload = image3.onload = function(e) {
    loaded++;
    if (loaded === numOfImages)
        draw();   // <-- second part of chain, invoke loop
}

//show if any error occurs
image0.onerror = image1.onerror = 
image2.onerror = image3.onerror = function(e) {
    console.log(e);
}

//invoke async loading... you can put these four into your
//window.onload if you want to
image0.src = "img/image0.png";
image1.src = "img/image1.png";
image2.src = "img/image2.png";
image3.src = "img/image3.png";

// this is the main function
function draw() {

    var images = new Array(image0, image1, image2, image3),
        counter = 0,
        delayInMilliseconds = 4000,
        maxNum = images.length - 1,

        myCanvas = document.getElementById('myCanvas'),
        ctx = myCanvas.getContext('2d'),

        me = this; //this we need for setTimeout()

    //third part of chain, have a function to invoke by setTimeout
    this._draw = function() {

        //if the next image will cover the canvas
        //there is no real need to clear the canvas first.
        //I'll leave it here as you ask for this specifically
        ctx.clearRect(0, 0, myCanvas.width, myCanvas.height)
        ctx.drawImage(images[counter++], 0, 0);
        if (counter > maxNum) counter = 0;

        setTimeout(me._draw, delayInMilliseconds); //use me instead of this
    }
    this._draw(); //START the loop
}

Working demo here: 这里的工作演示:
http://jsfiddle.net/AbdiasSoftware/dhxNz/ http://jsfiddle.net/AbdiasSoftware/dhxNz/

The _draw() is wrapped in draw() to localize the variables and also to make sure that _draw() doesn't end up on the window object. _draw()包装在draw()以对变量进行本地化,并确保_draw()不会最终出现在window对象上。 For the same reason we store a reference to this as this is changed to window object when its code is invoked. 出于同样的原因,我们存储的参考this因为this改变为window调用它的代码时对象。 me (or what you want to call it) makes sure that we are calling on the right object so that we have access to the local variables (canvas, ctx, counter etc.). me (或您要调用的名称)确保我们正在调用正确的对象,以便可以访问局部变量(画布,ctx,计数器等)。

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. 例如,请参阅: http//jsfiddle.net/8c9MM/1/ You can also pause the slideshow since we're assigning interval to the setInterval() function 您还可以暂停幻灯片放映,因为我们正在为setInterval()函数分配间隔

How about this. 这个怎么样。

HTML 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 使用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. 这是我第一次用javascript创建任何东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM