Curve speed down using Math.PI - javascript

I am trying to create a countdown that starts at 100 and ends at 30.
I want the beginning of the countdown to be really fast and then slow down and stop at 30. At the moment though the "path" of the countdown is linear.
Also to make you aware, the start number and end number may change, but still require a curved time effect.
https://jsfiddle.net/ygnvav7c/
$(document).ready(function() {
var timer;
var count=100;
var ms = 1;
var step = 5;
var counter = setTimeout(timer, ms);
Countdown();
function Countdown() {
count=count - 1;
if (count >= 30) {
$('#countdown-display').html(count);
ms = ms + step;
counter = setTimeout(Countdown, ms);
}
}
});
How can I use Math.PI to make the time "curve"?

Parameterize your count variable with some number 0 <= t <= 1. Increment this with a regular interval (say 0.01)
e.g. for a quadratic decay:
count = count_start + (count_end - count_start) * (1 - t) ^ 2
For a sine-curve decay:
count = count_start + (count_end - count_start) * sin(pi * t / 2)
For an exponential decay:
count = count_start + (count_end - count_start) * (1 - k ^ t) / (1 - 1 / k)
where k > 1

As much as I appreciate the solutions of my dear collegues but the requirement was that it stops at the end. So the time must go to infinite or at least close enough.
Taking 10^6 as "close enough" and the tangent function as the means to reach infinity (but not to go beyond) together with a bit of simple algebra and taking the liberty to change the counting function from decreasing to increasing we get the following to play with-not to to forget the complete ignorance of teh good ol' Chicago Manual of Style by using an infamously long and winded run-on sentence, that is additionally peppered with a lot of redundant additions.
tl;dr: We need to get the x in tan(x) as close to PI/2 as possible but not bigger to reach that goal.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Counter</title>
<script type="text/javascript">
var timer;
var counter = setTimeout(timer, ms);
var ms = 1;
var count_start = 30;
var count_end = 100;
var count = count_start;
// roughly (atan(10^6) - .02) / 68
var step = 0.0228058;
// to avoid zero
var start = 0.02;
var brk = document.createElement("br");
function Countdown() {
var out = document.getElementById("output");
var text;
if (++count < count_end) {
start += step;
ms = Math.tan(start) * count;
counter = setTimeout(Countdown, ms);
text = count.toString() + " - " + ms.toString();
text = document.createTextNode(text);
out.appendChild(text);
out.appendChild(brk.cloneNode());
}
}
</script>
</head>
<body onload="Countdown()">
<p id="output" > </p>
</body>
</html>
Reducing the value in step makes the curve end earlier (you kind of "zoom in"), e.g.: step = 0.02 lets the final step last about half a second and about 85ms with step = 0.01.

Related

Increment to a value from 0 in 5 seconds

I need to increment the value from 0 to 103551 in 5 seconds. Below is the logic I have used. But it's not incrementing to the required value within 5 secs
var counter = 0;
var el = document.getElementById('seconds-counter');
function incrementSeconds() {
counter += 1;
el.innerText = "Processing " + counter + " execution records";
if(counter == 103551) {
console.log(new Date());
}
}
console.log(new Date());
var time = 103551/5000;
var cancel = setInterval(incrementSeconds, time);
HTML
<div id='seconds-counter'> </div>
Your math is a little off. 103551/5000 = 20.7102ms
1000ms = 1s, 5s = 5000ms, 5000/20.7102 = 240 iterations.
The equation you want to solve is 5000/x = numIterations
So x = 5000/numIterations
Note: Most browsers have a minimum number you can set in setInterval(), so you may need to increment by more than 1 each loop to count to 103551 in 5 seconds. Since this is an oddly specific problem I'm going to gues that this might be an assignment so will omit a full solution from this answer. Good luck!

Requestanimationframe millisecond precision

I'm working on a game loop and can't get past a specific issue: A bunch of objects start with an incremented delay and should move a certain distance.
The expected behaviour is that all objects should move in an even diagonal line, yet they move in uneven groups.
I realize the issue lies in 16.667ms interval updates which "groups" objects in update cycles. Is it possible to achieve sub-17ms precision?
I have tried separating update and render methods and run the update inside a delta while loop - all to no avail.
Here's the relevant part from the tick function:
function tick() {
if (this._stopped) return;
let now = performance.now();
if (now < this._lastTick + this._interval - 1) {
this._rafId = requestAnimationFrame(this.tick);
return;
}
this._rafId = requestAnimationFrame(this.tick);
let frameTime = (now - this._lastTick);
this._lastTick = now;
this._delta += frameTime;
let acc = 0;
while (this._delta >= this._interval) {
this._delta -= this._interval;
//this.update(this._interval);
acc++;
}
this.update(acc * this._interval);
//this.render(time);
this.count++;
}
Here's the codepen.
Would really appreciate any input.

How to use the various rampToValueAtTime methods

When I use the following code to fade in a file it doesn't work as I expect. I expect a gradual fade in from 0 to 1 over the course of 5 seconds, instead I get an abrupt change five seconds into playing the file where the gain instantly goes from 0 to 1. What am I not understanding ?
soundObj.play = function() {
playSound.buffer = soundObj.soundToPlay;
playSound.connect(gainNode);
gainNode.gain.value = 0;
gainNode.gain.exponentialRampToValueAtTime(1, audioContext.currentTime + 5);
gainNode.connect(audioContext.destination);
playSound.start(audioContext.currentTime);
}
Update/Edit
I changed the above code to the following and it seems to work, now I am researching why. I've added a few comments. Mainly inquiring as to if adding a setValueAtTime method is necessary and if a non zero value is necessary for the gain.value properties default value.
soundObj.play = function() {
playSound.buffer = soundObj.soundToPlay;
playSound.connect(gainNode);
gainNode.gain.value = 0.001; // If set to 0 it doesn't fade in
gainNode.gain.setValueAtTime(gainNode.gain.value, audioContext.currentTime); // Is this needed to have the other RampToValue methods work ?
gainNode.gain.exponentialRampToValueAtTime(1, audioContext.currentTime + 7);
gainNode.connect(audioContext.destination);
playSound.start(audioContext.currentTime);
}
A non-zero positive value is necessary for exponentialRampToValueAtTime. This isn't a Web Audio thing as much as it's just a math thing.
There's really no way to exponentially grow a value of 0.
Here's a rough version of the algorithm Chrome uses (rewritten in JS):
// start value
var value1 = 0.1;
// target value
var value2 = 1;
// start time (in seconds)
var time1 = 0;
// end time (in seconds)
var time2 = 2;
// duration
var deltaTime = time2 - time1;
// AudioContext sample rate
var sampleRate = 44100;
// total number of samples
var numSampleFrames = deltaTime * sampleRate;
// time incrementer
var sampleFrameTimeIncr = 1 / sampleRate;
// current time (in seconds)
var currentTime = 0;
// per-sample multiplier
var multiplier = Math.pow( value2 / value1, 1 / numSampleFrames );
// output gain values
var values = new Array( numSampleFrames );
// set up first value
var value = value1 * Math.pow( value2 / value1, ( ( currentTime - time1 ) * sampleRate ) / numSampleFrames );
for ( var i = 0; i < numSampleFrames; ++i ) {
values[ i ] = value;
value *= multiplier;
currentTime += sampleFrameTimeIncr;
}
If you change value1 to zero, you'll see that the output array is basically full of NaN. But Chrome also adds a bit of extra code to save you from that by special-casing instances where your value is <= 0 so that you don't actually end up with gain values of NaN.
If none of that makes sense, let me put it this way. In order to exponentially grow a value, you basically need a loop that looks like this:
for ( var i = 0; i < length; ++i ) {
values[ i ] = value;
value *= multiplier;
}
But if your initial value is 0, well, 0 multiplied by any other number is always 0.
Oh, and if you're interested (and can read C++), here's a link to the code that Chrome uses: https://chromium.googlesource.com/chromium/blink/+/master/Source/modules/webaudio/AudioParamTimeline.cpp
Relevant stuff is on line 316.
Edit
Apologies for a Chrome-centric explanation. But the underlying math concept of not being able to exponentially grow a value of zero will hold with any implementation.

Random adding within a certain time to a certain number

I need help with something I'm working on in JavaScript/jQuery
I'd like to give a set 'destination' number, and give it a set duration, and have it so it adds up in intervals of 1 randomly throughout the duration (not equally spaced, but not all at the start, end or middle), but reaching the 'destination' number by the duration of time is up.
So, if I set a duration of 20 seconds, and a 'destination' number of 10. It will start the timer, and randomly add in intervals of 1 (following no pattern), and the duration finishes at the same time as the last number is added.
I'm really stuck with this, and not sure where to even begin.
Any help at all would be greatly appreciated, thanks a lot!
My approach is:
Divide the duration to equal pieces (one for each increment, starting with 0, ending with the full duration)
Randomize the delays, but keep the same sum. If you add some random value to one delays, then subtract the same amount from delays interval.
Call window.setTimeout with all the delays. Give it a function witch increments the current value by one.
The code:
var start = parseInt($("#inStart").val(), 10),
end = parseInt($("#inEnd").val(), 10),
duration = parseInt($("#inDuration").val(), 10),
difference = end - start,
current = start - 1,
step = duration * 1000 / difference,
delays = [],
index, amount,
increment = function () {
current += 1;
$("#outCurrent").text(current);
};
// calculate equal delays
for (index = 0; index <= difference; index++) {
delays.push(step * index);
}
// randomize delays, without changing the sum
for (index = 1; index < delays.length - 2; index++) {
amount = (Math.random() - 0.5) * step;
delays[index] -= amount;
delays[index+1] += amount;
}
// schedule the increment calls
for (index = 0; index < delays.length; index++) {
window.setTimeout(increment, delays[index]);
}
Here is a demo fiddle, you can try it out.

Why does this Increment not work?

I'm having some problems with this code. My problem is that with the code below, it doesn't plus the detection-ratio text with the 'incr'. It just enters the incr, but doesn't plus.
This is my code.
(function loop() {
var rand = Math.round(Math.random() * (3000 - 500)) + 500;
var incr=Math.floor(Math.random()*6);
setTimeout(function() {
document.getElementById('detection-ratio').innerText = '0 / '+ ++incr;
loop();
}, rand);
}());
The 'detection-ratio' text looks like this as default:
0 / 0
Then, lets say 'incr' generates the number '3', then it should increase the last 0 with 3, so it would look like this:
0 / 3
Then, lets say it's going to generate a new 'incr', lets say '5'. Then it would look like this:
0 / 8
---> But right now, it doesn't do that. It just writes the 'incr' into the 'detection-ratio' without increasing it.
Hope this code would help you to get the expected output, let me know if something breaks. Also stop iteration once it reaches > 26
var incr = 0;
(function loop() {
var rand = Math.round(Math.random() * (3000 - 500)) + 500;
incr += Math.floor(Math.random()*6);
setTimeout(function() {
console.log('0 / '+ incr);
loop();
}, rand);
}());
Thanks for the explanation and patience.
I am assuming you are trying to append text to detection-ratio
if so you need to
document.getElementById('detection-ratio').innerText += '0 / '+ incr;
++ before a variable is a pre-increment operator, since you are generating random numbers i am assuming that is not actually what you want.
Since you're calling the loop recursively anyway, you may want to consider a more functional approach:
(function loop(startctr) {
var rand = Math.round(Math.random() * (3000 - 500)) + 500;
nextctr = startctr + Math.floor(Math.random()*6);
setTimeout(function() {
console.log('0 / '+ nextctr);
loop(nextctr);
}, rand);
}(0));

Categories