使用 'setInterval' 和 'setTimeout' 循环不起作用
Loop with 'setInterval' and 'setTimeout' doesn't work
我正在尝试制作一些卡片的动画,这些卡片应该从右侧进入屏幕,在中间停顿一会儿,然后向左消失,无限循环。
这是我试过的:
function startAnimation(elem) {
$('#' + elem).fadeIn(150).animate({
left: '0'
}, 1500);
}
function endAnimation(elem) {
$('#' + elem).animate({
left: '-200%'
}, 1500);
$('#' + elem).fadeOut(100).animate({
left: '200%'
}, 300);
}
function scrollCards(elem, n) {
startAnimation(elem);
setTimeout(function() {
endAnimation(elem);
}, 700);
elem += 1;
elem = elem == n ? 0 : elem;
return elem;
}
n = 3;
var card = 0
var firstAnimationDone = false;
$('#0').fadeIn(150);
setInterval(function() {
if (!firstAnimationDone) {
endAnimation(card);
card = 1;
}
card = scrollCards(card, n);
firstAnimationDone = true;
}, 4500);
/* (boxArticle is here just to keep static the part of the page where the animation takes place) */
.boxArticle {
overflow: hidden;
height: 100px;
}
.boxAchievements {
position: relative;
height: 100px;
width: 100%;
left: 200%;
top: 5px;
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="boxArticle">
<article class="boxAchievements" id="0">
<h2>My achievements</h2>
<p>Write 1</p>
</article>
<article class="boxAchievements" id="1">
<h2>My achievements</h2>
<p>Write 2</p>
</article>
<article class="boxAchievements" id="2">
<h2>My achievements</h2>
<p>Write 3</p>
</article>
</div>
当我将 setTimeout
添加到 scrollCards
函数时,它会在中间停止很长时间,无论我在方法中输入的间隔有多长,它都会使循环不同步, 所以我有 2 张卡片同时移动。
您可以使用 CSS animation
规则来用更少的代码实现您想要的。下面的解决方案使用了一个技巧,使无限动画能够 运行 并在迭代之间有延迟(例如,参见 )。
简而言之,动画持续时间的设置考虑了延迟,@keyframes
通过保持相同的动画 属性 值从某个点到 100%(即如果需要 2 秒,并且延迟为8s,然后将持续时间设置为8+2=10s并完成属性变化100*2/10=20%)。
然后您可以随时添加 class 和 animation
。要对齐动画,请按顺序添加 classes,步长等于:duration + delay / number of elements.
请注意,由于删除了 fadeIn
/ fadeOut
方法调用和 display: none;
规则,您的 CSS 已更改为正确对齐 <article>
元素。
(() => {
$('#0').addClass("middle");
setTimeout(() => $("#1").addClass("middle"), 5e3);
setTimeout(() => $("#2").addClass("middle"), 9e3);
})();
body {
margin: 0;
}
:root {
--middle : calc(50% - 25vw / 2);
--left : calc(0% - 25vw);
--duration : 12s;
}
.boxArticle {
position: relative;
overflow: hidden;
height: 100vh;
width: 100vw;
}
.boxAchievements {
position: absolute;
height: 100px;
width: 25vw;
left: 200%;
top: 5px;
}
.middle {
animation:
middle var(--duration) linear 0s normal infinite forwards running,
left var(--duration) linear 0s normal infinite forwards running;
}
@keyframes middle {
8.3%, 100% { left: var(--middle); }
}
@keyframes left {
8.3%, 24.9% { left: var(--middle); }
33.2%, 100% { left: var(--left); }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="boxArticle">
<article class="boxAchievements" id="0">
<h2>My achievements</h2>
<p>Write 1</p>
</article>
<article class="boxAchievements" id="1">
<h2>My achievements</h2>
<p>Write 2</p>
</article>
<article class="boxAchievements" id="2">
<h2>My achievements</h2>
<p>Write 3</p>
</article>
</div>
还有一些关于您代码段中代码的注释:
不要混用变量类型。尽管 JavaScript 允许这样做,但这对于任何将阅读您的代码(包括一年后的您)的人来说都是噩梦之源。特别是,scrollCards
有一个参数 elem
,它应该是 Element
,而不是 number
(反之亦然)。
使用 recursive setTimeout
而不是 setInterval
- 后者排队函数调用 不管 是否之前的动画是否完成(还有其他原因使用递归 setTimeout
超出问题范围)。
用 var
声明 n
(更好的是 - 不要声明任何全局变量,但至少避免通过省略声明关键字来创建 implied globals)。
setTimeout
调用不能保证在指定的时间后 运行 因为它们是异步的 - 根据页面加载,完全不同步动画的风险随着时间。
缓解这种情况的一种方法是使用 promises 等待超时触发,但将项目动画与之对齐可能是一项艰巨的任务。作为示例,以下是如何让 scrollCards
等待 endAnimation
发生:
(() => {
const now = () => new Date().toISOString();
const startAnimation = (elem) => console.log(`started animation at ${now()}`);
const endAnimation = (elem) => console.log(`ended animation at ${now()}`);
async function scrollCards(elem, n) {
startAnimation(elem);
//assuming endAnimation is synchronous
await new Promise((resolve) => setTimeout((elem) => resolve(endAnimation(elem)), 700, elem));
elem += 1; //see #1 - this is error-prone
elem = elem == n ? 0 : elem;
return elem;
};
scrollCards(0,1);
})();
我正在尝试制作一些卡片的动画,这些卡片应该从右侧进入屏幕,在中间停顿一会儿,然后向左消失,无限循环。 这是我试过的:
function startAnimation(elem) {
$('#' + elem).fadeIn(150).animate({
left: '0'
}, 1500);
}
function endAnimation(elem) {
$('#' + elem).animate({
left: '-200%'
}, 1500);
$('#' + elem).fadeOut(100).animate({
left: '200%'
}, 300);
}
function scrollCards(elem, n) {
startAnimation(elem);
setTimeout(function() {
endAnimation(elem);
}, 700);
elem += 1;
elem = elem == n ? 0 : elem;
return elem;
}
n = 3;
var card = 0
var firstAnimationDone = false;
$('#0').fadeIn(150);
setInterval(function() {
if (!firstAnimationDone) {
endAnimation(card);
card = 1;
}
card = scrollCards(card, n);
firstAnimationDone = true;
}, 4500);
/* (boxArticle is here just to keep static the part of the page where the animation takes place) */
.boxArticle {
overflow: hidden;
height: 100px;
}
.boxAchievements {
position: relative;
height: 100px;
width: 100%;
left: 200%;
top: 5px;
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="boxArticle">
<article class="boxAchievements" id="0">
<h2>My achievements</h2>
<p>Write 1</p>
</article>
<article class="boxAchievements" id="1">
<h2>My achievements</h2>
<p>Write 2</p>
</article>
<article class="boxAchievements" id="2">
<h2>My achievements</h2>
<p>Write 3</p>
</article>
</div>
当我将 setTimeout
添加到 scrollCards
函数时,它会在中间停止很长时间,无论我在方法中输入的间隔有多长,它都会使循环不同步, 所以我有 2 张卡片同时移动。
您可以使用 CSS animation
规则来用更少的代码实现您想要的。下面的解决方案使用了一个技巧,使无限动画能够 运行 并在迭代之间有延迟(例如,参见
简而言之,动画持续时间的设置考虑了延迟,@keyframes
通过保持相同的动画 属性 值从某个点到 100%(即如果需要 2 秒,并且延迟为8s,然后将持续时间设置为8+2=10s并完成属性变化100*2/10=20%)。
然后您可以随时添加 class 和 animation
。要对齐动画,请按顺序添加 classes,步长等于:duration + delay / number of elements.
请注意,由于删除了 fadeIn
/ fadeOut
方法调用和 display: none;
规则,您的 CSS 已更改为正确对齐 <article>
元素。
(() => {
$('#0').addClass("middle");
setTimeout(() => $("#1").addClass("middle"), 5e3);
setTimeout(() => $("#2").addClass("middle"), 9e3);
})();
body {
margin: 0;
}
:root {
--middle : calc(50% - 25vw / 2);
--left : calc(0% - 25vw);
--duration : 12s;
}
.boxArticle {
position: relative;
overflow: hidden;
height: 100vh;
width: 100vw;
}
.boxAchievements {
position: absolute;
height: 100px;
width: 25vw;
left: 200%;
top: 5px;
}
.middle {
animation:
middle var(--duration) linear 0s normal infinite forwards running,
left var(--duration) linear 0s normal infinite forwards running;
}
@keyframes middle {
8.3%, 100% { left: var(--middle); }
}
@keyframes left {
8.3%, 24.9% { left: var(--middle); }
33.2%, 100% { left: var(--left); }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="boxArticle">
<article class="boxAchievements" id="0">
<h2>My achievements</h2>
<p>Write 1</p>
</article>
<article class="boxAchievements" id="1">
<h2>My achievements</h2>
<p>Write 2</p>
</article>
<article class="boxAchievements" id="2">
<h2>My achievements</h2>
<p>Write 3</p>
</article>
</div>
还有一些关于您代码段中代码的注释:
不要混用变量类型。尽管 JavaScript 允许这样做,但这对于任何将阅读您的代码(包括一年后的您)的人来说都是噩梦之源。特别是,
scrollCards
有一个参数elem
,它应该是Element
,而不是number
(反之亦然)。使用 recursive
setTimeout
而不是setInterval
- 后者排队函数调用 不管 是否之前的动画是否完成(还有其他原因使用递归setTimeout
超出问题范围)。用
var
声明n
(更好的是 - 不要声明任何全局变量,但至少避免通过省略声明关键字来创建 implied globals)。setTimeout
调用不能保证在指定的时间后 运行 因为它们是异步的 - 根据页面加载,完全不同步动画的风险随着时间。缓解这种情况的一种方法是使用 promises 等待超时触发,但将项目动画与之对齐可能是一项艰巨的任务。作为示例,以下是如何让
scrollCards
等待endAnimation
发生:
(() => {
const now = () => new Date().toISOString();
const startAnimation = (elem) => console.log(`started animation at ${now()}`);
const endAnimation = (elem) => console.log(`ended animation at ${now()}`);
async function scrollCards(elem, n) {
startAnimation(elem);
//assuming endAnimation is synchronous
await new Promise((resolve) => setTimeout((elem) => resolve(endAnimation(elem)), 700, elem));
elem += 1; //see #1 - this is error-prone
elem = elem == n ? 0 : elem;
return elem;
};
scrollCards(0,1);
})();