Based on this SO answer: Changing the interval of SetInterval while it's running
I created this mini Svelte-Repl: https://svelte.dev/repl/ae3987eff26446b49af4a85d029acd80?version=3.49.0
Which looks like this:
<script>
let display = 100
let interval = 1;
let myFunction = function() {
display--
interval *= 1.07;
setTimeout(myFunction, interval);
}
setTimeout(myFunction, interval);
</script>
{#if display > 0}
{display}
{:else}
End
{/if}
So the function myFunction
calls itself while changing (increasing) the interval so that the count-down slows down. Now I would like to count down from 100 and slowly make the interval longer. I was thinking about the Math on how to achieve that, but couldn't work it out. I'd be grateful for any tips:)
Update:
I guess the question is kind of badly expressed. The idea was to:
- Have a decreasing counter (
display
) from 100 to 0 - The speed in which this counter decreases should become smaller, meaning that changing from e.g. 90 to 89 takes less time than from 10 to 9
- The function in which it decreases does not matter. It can be exponentially, linearly or any other function. My aim was more about knowing how to solve this "generally"
- The entire process, decreasing the variable
display
from 100 to 0, should take (can also be roughly) 10 seconds. - How would I do the Math?
CodePudding user response:
Instead of changing the interval times, have a fast rate (frame rate, with requestAnimationRate
), and calculate the counter's value based on the timestamp (i.e. the time that has elapsed since the start of the count down).
I would go for a formula that does not lead to exponentially increasing delays, but a less "drastic" one. For instance, you could use this formula to translate elapsed time to a counter value:
initialCount - r * sqrt(elapsed_time)
The coefficient r
is then determined by the requirement that after 10 seconds this expression should be zero, so that brings us to:
r = initialCount / sqrt(total_duration)
Instead of a square root (i.e. exponent 0.5), you could use another exponent. The derivation of the value of r
remains the same then (using that exponent).
Here is an implementation, where I have also displayed the elapsed time, so to verify it works as intended:
let [span, control] = document.querySelectorAll("span");
function countDown(counterStart, duration) {
const exp = 0.3; // Between 0 and 1: determines the gravity of the slow-down
let r = counterStart / duration ** exp;
let startTime;
function loop(timestamp) {
if (!startTime) startTime = timestamp;
const elapsed = timestamp - startTime;
const counter = Math.ceil(counterStart - r * elapsed ** exp);
span.textContent = Math.max(0, counter);
control.textContent = Math.floor(elapsed / 100) / 10;
if (counter > 0) requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
}
countDown(100, 10000);
Count down: <span></span><br>
Elapsed time: <span></span>
If you wish to stick with exponentially increasing delays, then use a logarithm instead of a root:
initialCount - r * log(elapsed_time)
The code is very similar:
let [span, control] = document.querySelectorAll("span");
function countDown(counterStart, duration) {
let r = counterStart / Math.log(duration);
let startTime;
function loop(timestamp) {
if (!startTime) startTime = timestamp;
const elapsed = timestamp - startTime;
const counter = Math.ceil(counterStart - r * Math.log(elapsed));
span.textContent = Math.max(0, counter);
control.textContent = Math.floor(elapsed / 100) / 10;
if (counter > 0) requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
}
countDown(100, 10000);
Count down: <span></span><br>
Elapsed time: <span></span>