0

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?
Lenn
  • 1,283
  • 7
  • 20
  • 3
    I am always surprised to read this kind of method to develop a setInterval with variation of durations, whereas the methods setInterval and setTimeout have never offered a guarantee on their durations of their delays, it is also documented in the documentation . – Mister Jojo Jul 27 '22 at 10:11
  • 1
    Naming a delay variable 'counter' while trying to produce a countDown is something irrational and confusing – Mister Jojo Jul 27 '22 at 10:17
  • true, i'll change that – Lenn Jul 27 '22 at 10:18
  • 1
    Do you really want the delay to increase exponentially? – trincot Jul 27 '22 at 10:19
  • And you are also right about their durations...:/ Is there any other way to achieve a de-accelerating counter that takes exactly 10 seconds? – Lenn Jul 27 '22 at 10:19
  • @trincot I do not mind if it's exponentially or linearly or of any other form:) – Lenn Jul 27 '22 at 10:20
  • 1
    There are countless ways to have a de-accelerating counter, each with their own characteristics. Just like there are many curves that go down... – trincot Jul 27 '22 at 10:20
  • yes you are right, I just did not know where to start or how to solve this with any kind of function, regardless of its type – Lenn Jul 27 '22 at 10:22
  • 1
    yes, there is other ways, you need tu use timestanp testing (or maybe with css?). and please make a better delay progress computation than an exponetial equation – Mister Jojo Jul 27 '22 at 10:25
  • 1
    You function should be something like `StartSpeed * Acceleration^X` I think. You should be able calculate the Acceleration with that – Lalaluka Jul 27 '22 at 10:27
  • your question is poorly worded. what does it mean to slow down the periods between your display's ? --from what initial period? --Is 10 seconds the total duration of seconds from 100 to zero? --Is 10 seconds the duration of the last period between 1 and zero? – Mister Jojo Jul 27 '22 at 10:45
  • @MisterJojo You are right! I'll update the question – Lenn Jul 27 '22 at 10:55
  • your min problem is about adding a period n(100), like `1 + 2 + 3 + 4...` until `100` see https://en.wikipedia.org/wiki/1_%2B_2_%2B_3_%2B_4_%2B_%E2%8B%AF – Mister Jojo Jul 27 '22 at 11:32

1 Answers1

3

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>
trincot
  • 317,000
  • 35
  • 244
  • 286