You could use the CSS animation rule instead to achieve what you want with much less code. The solution below uses a trick that enables infinite animation to run with a delay between iterations (see, this Q&A, for example).
In short, animation duration is set with delay in mind, and @keyframes controls the delay by keeping the same animated property value from some point to 100% (i.e. if it takes 2s, and the delay is 8s, then set the duration to 8+2=10s and finish the property change by 100*2/10=20%).
Then you add the class with animation whenever you want. To align animations, add classes in sequence with a step equal to: duration + delay / number of elements.
Note that your CSS is changed to properly align <article> elements because of the removal of fadeIn / fadeOut method calls and display: none; rule.
(() => {
$('#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>
There are also a couple of notes about the code in your snippet:
Do not mix types of variables. Although JavaScript allows that, this is a source of nightmares for anyone who will read your code (including you a year from now). In particular, scrollCards has a parameter elem which is supposed to be an Element, not a number (or vice versa).
Use a recursive setTimeout rather than setInterval - the latter queues up a function call regardless of whether the previous animation is finished or not (there are other reasons to use recursive setTimeout that are outside of the question scope).
Declare n with var (better still - do not declare any global variables, but at least avoid creating implied globals by omitting a declaration keyword).
setTimeout calls are not guaranteed to run after a specified amount of time as they are asynchronous - depending on a page load, the risk of completely desynchronized animations increases with time.
One way to mitigate that is to use promises to wait until the timeout fires, but aligning item animations with that will likely be a difficult task. As an illustration, here is how you make scrollCards wait for endAnimation to happen:
(() => {
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);
})();