[英]Simplest slideshow in HTML5 canvas, canvas.context.clearRect not working with setTimeout
这是一个非常简单的幻灯片演示的代码,该演示应在4秒钟内显示4张图像,每秒显示一张图像。 取而代之的是,我得到了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>
更新:答案是:
在上面的代码中,我错误地假设getTimeout
函数是同步的,也就是说,我期望在调用该函数时,程序执行将停止,等待1000毫秒,然后调用draw_next_image
,然后才执行ctx.clearRect
。
实际上,JavaScript不能那样工作。 实际上, getTimeout
是异步的,即getTimeout
设置Timeout并几乎立即返回,并且代码执行继续,因此ctx.clearRect
被调用,并且实际上在 draw_next_image
之前被draw_next_image
。 因此,在Timeout到期并调用draw_next_image
,代码的执行可能会到达任意代码行。 在我的情况下,所有4个clearRect
几乎都将在超时到期之前几乎同时被调用。 然后1000毫秒后,所有4个超时将几乎立即一个接一个地到期,并且所有4个图像也几乎都将在同一时间绘制,而没有clearRects
,该对象早已执行。
问题是在代码中,您将异步函数视为同步函数。
要点如下:
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()
包装在draw()
以对变量进行本地化,并确保_draw()
不会最终出现在window
对象上。 出于同样的原因,我们存储的参考this
因为this
改变为window
调用它的代码时对象。 me
(或您要调用的名称)确保我们正在调用正确的对象,以便可以访问局部变量(画布,ctx,计数器等)。
您有一些问题,包括错误命名图像变量。
尝试这个:
// 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()
函数分配间隔
这个怎么样。
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创建任何东西。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.