2

これは非常に単純なスライドショーのコードで、4 秒間に 4 つの画像を 1 秒間に 1 画像ずつ表示する必要があります。代わりに、4 秒の遅延が発生し、すべての画像が重ねて描画されます。私は何を間違っていますか?

<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>

更新: 答えは:

上記のコードでは、誤ってgetTimeoutfunction が同期的であると想定していました。つまり、呼び出し時にプログラムの実行が停止し、1000 ミリ秒待機してから を呼び出してから実行すると予想していましdraw_next_imagectx.clearRect

実際には、Javascript はそのようには機能しません。実際getTimeoutには非同期です。つまりgetTimeout、 Timeout を設定してほぼ瞬時に戻り、コードの実行が続行されるため、ctx.clearRectはすぐに、実際にはの前に呼び出されdraw_next_imageます。そのため、Timeout が切れて が呼び出さdraw_next_imageれるまでに、コードの実行が任意のコード行に到達する可能性があります。私の場合clearRect、タイムアウトの期限が切れるずっと前に、4 つすべてがほぼ同時に呼び出されます。それから 1000 ミリ秒後、4 つのタイムアウトすべてが次々とすぐに期限切れになり、4 つの画像すべてがほぼ同時に描画されclearRectsます。これはかなり前に実行されました。

4

3 に答える 3

3

問題は、コード内で非同期関数を同期のように扱っていることです。

主なポイントは次のとおりです。

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

これらの呼び出しは呼び出されるとすぐに失敗するため、コードはこれらの結果が (おそらく) 準備される前に次のステップの実行を開始します。

そのため、イベントに基づいて呼び出しをチェーンする必要があります。次に例を示します。

//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
}

ここでの動作デモ:
http://jsfiddle.net/AbdiasSoftware/dhxNz/

変数をローカライズし、それがオブジェクトに終わらないことを確認するために_draw()ラップされています。同じ理由で、コードが呼び出されたときに object に変更されるように参照を保存します。(またはあなたがそれを呼びたいもの)は、ローカル変数(canvas、ctx、counterなど)にアクセスできるように、正しいオブジェクトで呼び出していることを確認します。draw()_draw()windowthisthiswindowme

于 2013-06-05T04:14:29.477 に答える
1

画像変数の名前が間違っているなど、いくつかの問題があります。

これを試して:

// 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;

たとえば、 http: //jsfiddle.net/8c9MM/1/を参照してください。setInterval()関数に間隔を割り当てているため、スライドショーを一時停止することもできます

于 2013-06-05T04:10:40.533 に答える
1

これはどう。

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);

コードがあまり整理されていない場合は申し訳ありません。JavaScriptで何かを作るのはこれが初めてです。

于 2015-03-03T21:19:59.077 に答える