HTML Code:
<div class="container">
<div>
<h1 class="scrambleanimation">Creative Agency</h1>
<h1 class="scrambleanimation">Creative Agency</h1>
<h1 class="scrambleanimation">Creative Agency</h1>
<h1 class="scrambleanimation">Creative Agency</h1>
<h1 class="scrambleanimation">Creative Agency</h1>
<h1 class="scrambleanimation">Creative Agency</h1>
</div>
<div>
<h1 class="scrambleanimation">Creative Agency</h1>
<h1 class="scrambleanimation">Creative Agency</h1>
<h1 class="scrambleanimation">Creative Agency</h1>
<h1 class="scrambleanimation">Creative Agency</h1>
<h1 class="scrambleanimation">Creative Agency</h1>
<h1 class="scrambleanimation">Creative Agency</h1>
</div>
</div>
CSS Code:
.container {
display: flex;
height: 100vh;
width: 100%;
justify-content: space-evenly;
align-items: center;
}
.scrambleanimation {
position: relative;
}
.scrambleanimation > span {
/* content: ''; */
height: 100%;
width: 100%;
position: absolute;
left: 0%;
top: 0;
background-color: white;
z-index: 4;
}
JavaScript Code:
// phrase used one by one (controlled by k variable )
const phrases = [
"Design + Code",
"Defying Norms",
"f*ck Average",
"Work With Us",
];
const elements = document.querySelectorAll(".scrambleanimation");
elements.forEach((item, index) => {
item.innerHTML = " ";
});
elements.forEach((item, index) => {
let myFirstInterval;
let mySecondInterval;
let i = 0;
let j1 = -100;
let j2 = 0;
let k = 0;
// animation variable is to run animation1 and animation2 alternatively, it increments to 1 and is set back to 0 again.
let animationCount = 0;
// animation1
const animation1 = (phrase) => {
const length = phrase.length;
// clear previous intervals
if (myFirstInterval !== undefined) {
clearInterval(myFirstInterval);
}
if (mySecondInterval !== undefined) {
clearInterval(mySecondInterval);
}
// this is just like the typing effect but without typing cursor
setTimeout(() => {
myFirstInterval = setInterval(() => {
item.innerHTML = phrase.slice(0, i);
if (i >= length) {
clearInterval(myFirstInterval);
i = 0;
}
i++;
}, 100);
}, index * 200);
// here I create a span which is absolute in the css and left -100% then slides in (creates something like erasing effect)
setTimeout(() => {
mySecondInterval = setInterval(() => {
item.innerHTML = phrase + "<span></span>";
const innerSpan = item.firstElementChild;
innerSpan.style.left = `${j1}%`;
j1 = j1 + 2;
if (j1 >= 1) {
j1 = -100;
innerSpan.style.left = `${j1}%`;
clearInterval(mySecondInterval);
}
}, 50);
}, 100 * length + index * 200);
// to use next phrase in the next run
if (k >= phrases.length - 1) {
k = 0;
} else {
k++;
}
// to use alternate animation in the next run
if (animationCount >= 1) {
animationCount = 0;
} else {
animationCount++;
}
};
// animation2
const animation2 = (phrase) => {
const length = phrase.length;
// clear previous animations
if (myFirstInterval !== undefined) {
clearInterval(myFirstInterval);
}
if (mySecondInterval !== undefined) {
clearInterval(mySecondInterval);
}
// same like animation 1
setTimeout(() => {
myFirstInterval = setInterval(() => {
item.innerHTML = phrase.slice(0, i);
if (i >= length) {
clearInterval(myFirstInterval);
i = 0;
}
i++;
}, 100);
}, index * 200);
// erasing part is different in animation2, it randomly replaces characters with empty spaces
setTimeout(() => {
j2 = 0;
mySecondInterval = setInterval(() => {
let x = item.innerHTML;
x = setCharAt(x, Math.floor(Math.random() * length), " ");
item.innerHTML = x;
if (j2 >= length / 2) {
clearInterval(mySecondInterval);
j2 = 0;
}
j2++;
}, 200);
}, 100 * length + 14 * 200);
// to use next phrase in next run
if (k >= phrases.length - 1) {
k = 0;
} else {
k++;
}
// to use alternate animation in next run
if (animationCount >= 1) {
animationCount = 0;
} else {
animationCount++;
}
};
const animationInterval = (phrase) => {
if (animationCount == 0) {
animation2(phrase);
} else if (animationCount == 1) {
animation1(phrase);
}
};
animationInterval(phrases[k]);
// main interval
setInterval(
() => animationInterval(phrases[k]),
8000
);
});
live link: https://syedaffan.webflow.io/agency
it works fine on first render, but if stayed on the page for a min, it starts to glitch. and also when come back to the tab.
I have tried to explain everything I did in the code in the code comments.
gone through a lot of setInterval questions on stackoverflow but could not find anything, been trying to debug myself but it's still working like this.
This won't be precise when using setTimeout
. What can be more reliable is using the system clock. You should sample it often calculating the time difference since last timestamp. You would need to adjust your code for the new style of loop of course.
Here's a very simplified example that "ticks" in sync every 100ms. (even if sampling is done at ~60fps).
const currentString = "Hello, Welcome to the game! Don't type the strings as they appear. "
let typingIntervalMS = 100;
let startTime = null;
let currentCharIndex = 0;
// timestamp is passed automatically by requestAnimationFrame
function startTypingLoop(timestamp) {
if (!startTime) {
startTime = timestamp
}
const elapsed = timestamp - startTime
if (elapsed >= typingIntervalMS) {
tick();
startTime = timestamp;
}
if (currentCharIndex >= currentString.length) {
return;
}
requestAnimationFrame(startTypingLoop);
}
function tick() {
const currentChar = currentString.charAt(currentCharIndex);
container.innerHTML += currentChar;
currentCharIndex++;
}
requestAnimationFrame(startTypingLoop);
<div id="container">
</div>