Problems
The problems in your script includes one major fault is that you are generating random numbers unaware from the fact that the generated number will be used to select a span
and hide it and it needs to be a valid index, and what actually happens that it keeps on generating numbers which may occur twice, in which case it will try to hide the hidden letter again, and the waiting period of trying to find the valid index which is also not hidden it sometime takes less time or sometimes more. so that is the real reason behind the hiding time not being the same.
Secondly, you are just running the animation and that's it there is no stopping of the script it is continuously running and loading your browser along with setInterrval()
which alone is not known to have mercy on your browser even if it is minimized or tab switched, you need to stop it once all spans are hidden.
What to do
Select the elements you have to hide.
Get elements in an array using .toArray()
in a var say elemArray
Start generating the random number and validate if it is a valid index for the elemArray
if not call it recursively until you find a valid index between the range [0 - elemArray.length]
.
When you find a valid index hide the element on that index and use splice
to remove that element from the elemArray
in this way you will hide every element once and into a random sequence
Now about animations, Say Hello to requestAnimationFrame()
requestAnimationFrame
function that allows you to create smooth and fluid animations in JavaScript without you actually having to worry about making it smooth and fluid. Just add a few calls to requestAnimationFrame
and your browser takes care of the rest. That's it. and it also helps to control the factors such as your laptop/ phone/tablet going into battery mode and halving its performance reduced. Factors such as another tab taking focus. Read More Here
And in the end, you have to stop the animation too so use the brother of requestAnimationFrame
function which is cancelAnimationFrame
see below I created a demo for you hope it helps you out.
var framesPerSecond = 10;
var letters = $(".mailFirst>h2 span");
var elemArray = letters.toArray();
// store your requestAnimatFrame request ID value
var requestId;
//the animation function
function animate() {
setTimeout(function() {
//save the id returned from the function to use it
//for canceling or stopping the animation
requestId = requestAnimationFrame(animate);
// animating/drawing code goes here
hideRandomWord();
//check if there are no more elements left to hide
if (!elemArray.length) {
stopAnimation(requestId);
}
}, 2000 / framesPerSecond);
}
//start animation
requestAnimationFrame(animate);
//function to hide a character / word
function hideRandomWord() {
var min = 0;
var max = Math.floor(elemArray.length);
//The maximum is exclusive and the minimum is inclusive
var rand = Math.floor(Math.random() * (max - min)) + min;
//if elements array is not empty
if (elemArray.length) {
//if the generated index is a valid index for the elements array
if (typeof elemArray[rand] !== 'undefined') {
//animate opacity
$(elemArray[rand]).animate({
opacity: 0
});
//remove the element from the elements array
elemArray.splice(rand, 1);
} else {
//call recursively it self if not valid index generated
hideRandomWord();
}
}
}
function stopAnimation(requestId) {
// use the requestID to cancel the requestAnimationFrame call
cancelAnimationFrame(requestId);
}
.mailFirst {
position: absolute;
top: 0;
left: 0;
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="mailFirst">
<h2>
<span> @ </span>
<span> E </span>
<span> - </span>
<span> M </span>
<span> a </span>
<span> i </span>
<span> l </span>
<span> @ </span>
</h2>
</div>
The first problem, the uneven hiding of the letters, is due to the nature of your random function.
It looks for a letter to randomly hide, hides it and selects another. But the random selection still includes letters that have already been hidden, so it's just hiding them again - which is an operation you can't see so it looks like nothing is happening.
You need to remove the letters from the array as they get hidden so they no longer get included in the random selection.
Steve has explained it well but here is the code for it.
<html>
<head>
<style>
.mailFirst {
position: absolute;
top: 0;
left: 0;
color: red;
}
</style>
</head>
<body>
<div class="mailFirst">
<h2>
<span> @ </span>
<span> E </span>
<span> - </span>
<span> M </span>
<span> a </span>
<span> i </span>
<span> l </span>
<span> @ </span>
</h2>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
var mail1 = $(".mailFirst h2");
var letters = mail1.children();
var numberOfSpans = letters.length;
var hiddenSpans = 0;
function changeOpacity() {
setTimeout(
function() {
var ind;
var opc;
do {
var ind = (Math.random() * letters.length | 0);
var opc = Number(letters.eq(ind).css("opacity"));
console.log("index: " + ind + " and opc: " + opc);
} while (opc != 1)
letters.eq(ind).animate({
opacity: 0
}, 500);
hiddenSpans++;
if (hiddenSpans < numberOfSpans) {
changeOpacity();
}
}, 500
);
}
$(document).ready(changeOpacity);
</script>
</body>
</html>