Accelerate number the closer that it reaches its max - javascript

I'm trying to simulate the speed of a progress bar. The progress bar displays the current progress in percentages (0-100). What I am trying to achieve is a function/modifier that takes will modify the actual progress, to have it slow in the beginning and faster toward the end (as it reaches a 100%)
So for example, if the actual progress is 5% lets say our modifier would modify it to 6%, and when progress reaches 20% lets say our modifier modifies it to 35% and at 60% our modifier reports 80%....till it reaches a 100
So, its barely modifying in the beginning but steadily increasing in velocity toward the end...
I'm not sure how to approach this, I cannot tween because I don't have a time frame I can tween against. I imagine I'd have to apply a mathematical function that would modify the value on the go. The following does not work, but I'm adding it here just to illustrate my point:
console.clear()
let updated = 0;
for(i=0; i <= 100; i++){
updated = (1 - Math.pow(i/101,2))
console.log(i + updated)
}
I appreciate any direction about how to approach this. Thank you

Here are two ways.
var i = 0;
setInterval(function() {
if (i <= 100) {
updated = i * (Math.log10(i) / 2);
document.getElementById("bar1").style.width = i + "%";
document.getElementById("bar2").style.width = (i * (Math.log10(i) / 2)) + "%";
document.getElementById("bar3").style.width = (i * i * i / 10000) + "%";
i++;
}
}, 50)
.container {
background: #ccc;
width: 50vw;
}
.bar {
background: #faa;
height: 1em;
width: 0%;
border-top: thin solid #ccc;
}
<div class="container">
<div id="bar1" class="bar">Linear</div>
<div id="bar2" class="bar"></div>
<div id="bar3" class="bar"></div>
</div>

Tweening functions are easier if you move between values of 0.0 and 1.0. This will allow for code similar to your's.
For example:
for (let i = 0; i <= 1; i += .1) {
console.log((i ** 2 *100).toFixed(2))
}
Accelerates quite quickly. Since 0 ** n is always 0 and 1 ** n is alway 1 you can freely pick exponents to change your speed while still keeping values between 0 and 1. :
0.00
1.00
4.00
9.00
16.00
25.00
36.00
49.00
64.00
81.00
100.00

Let's suppose you have an f function and you have a t repeat time in milliseconds, that is, the progress bar will do a step on every t. You can work like this:
var intervalID = setTimeout(f, t);
Now, let's think about how f looks like. If we want the progress bar to be slow at the beginning, but quicker at the end, then we need a function which is monotonously increasing in a steeper manner than a linear function. A linear complexity looks like this:
l(x) = aX + b
Its derivative is
l'(x) = a
which is a constant. We need a function which is increasing faster. Let's look at this function:
f(x) = x^2
f'(x) = 2x
So this will be good for you
function f() {
var result = x * x;
//Set progress bar to Math.min(result, 100)
if (result >= 100) clearInterval(intervalID);
}

Related

Javascript Math.random() returning very similar values

So I have elements that are position: absolute and then I use Math.random() to set their left:#random and top:#random position.
However a very weird thing is happening. It should be completely random, thus they should be placed completely randomly. However time and time again, they are placed very closely together. Instead of being spread apart.
however you can clearly see, their positions are indeed random:
Here is the code I use to generate them:
const Clouds = function(props) {
const clouds = []
for (let i = 0; i < props.cloudNum; i++) {
const style = {
position: 'absolute',
height: 50 * props.cloudSize + 'px',
top: Math.random() * 100 + '%',
left: Math.random() * 100 + '%',
}
clouds.push(<Cloud key={i} style={style} />)
}
return <div className={props.side}>{clouds}</div>
}
is there a temporal component to Math.random, and because they are generated in sequence their random numbers are similar?
In fact, although they look like similar numbers they are not (remember that you are multiplying by 100), this means that your space of random numbers goes from 0 to 100 (since the decimals in the drawing barely have value, as is the case that you ask).
Keep in mind that if your space is 100 clouds only generating 13 clouds there is more than a 50% probability that two clouds occupy the same position by the birthday problem.
https://en.wikipedia.org/wiki/Birthday_problem
It's a coincidence that you get similar value. Try as many times as you want with my snippet to test it your own.
Note that my objects are much smaller than yours, not having elements overlapping give a better sense of randomness. IMHO, if you are generating clouds (depends on purposes) it could be better to use perlin noise
const container = document.getElementById('container');
const Clouds = function() {
for (let i = 0; i <10; i++) {
let myCloud = document.createElement("div");
myCloud.style.height = '15px';
myCloud.style.width = '15px';
myCloud.style.position = 'absolute';
myCloud.style.background = '#fff';
myCloud.style.border = '1px solid #ccc';
myCloud.style.left = Math.random()*100+'%';
myCloud.style.top = Math.random()*100+'%';
container.appendChild(myCloud);
}
}
function generate() {
container.innerHTML = '';
Clouds();
}
#container {
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
background: red;
}
button {
position: absolute;
z-index: 999;
}
<div id="container"></div>
<button onClick="generate()">Generate</button>
There's no temporal component - it's just generated by the system. Here's a good thread explaining it. The random algorithm depends on the JavaScript engine (there's a V8 answer in that thread) but the function always produces a floating-point number between 0 and 1 inclusive. It's an incredibly large coincidence that your code yielded two numbers that close.

How to increment the number from 00.000000 to 48.853373 in few seconds by 0.00001

I am using map coordinates as a part of website logo. (2.378628, 48.853373).
What I want to do, is count both numbers from 0.000000 so they reach given points during the same time (3-5 seconds), incrementing by 0.000001. How is that possible? This crashes my computer, and setInterval does sth every ms, which is not enough.
while (i < 48.853373) {
i = i + 0.000001;
$('.js-center-lat').text(i);
}
Sounds like you want to "animate" the floating point number to count up to a defined value.
Have you considered third party libraries, like CountUp.js?
CountUp.js is a dependency-free, lightweight JavaScript "class" that can be used to quickly create animations that display numerical data in a more interesting way.
Quick example on how to use it:
var options = {
  useEasing: true,
  useGrouping: true,
  separator: '',
  decimal: '.',
};
var demo = new CountUp('myTargetElement', 0, 2.415543, 6, 2.5, options);
if (!demo.error) {
  demo.start();
} else {
  console.error(demo.error);
}
Hope this helps!
You take the time when you started the animation (startTime), and you have value how long the animation should last (duration).
For each animation step you can calculate the percentage of the animation.
And with that percentage you can count up multiple values.
let startTime = Date.now();
let duration = 3000;
function updateText() {
let percent = Math.min(1, (Date.now() - startTime) / duration);
// Math min ensures that percent does not become larger then 1
$('.val1').text(50 * percent); // 0 - 50
$('.val2').text(33 * percent); // 0 - 33
$('.val3').text(13 + 10 * percent); // 13 - 23
if (percent <= 1) {
requestAnimationFrame(updateText)
}
}
updateText();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="val1">
</div>
<div class="val2">
</div>
<div class="val3">
</div>

JavaScript continiously changing colour of SVG element - flickers

I have an element and I'm changing its colour with timer:
r = 0.05;
delta = 0.05;
function changecol(){
if (r < 0.05 || r > 0.95) delta = delta * (-1);
r += delta;
col = '#'+(Math.floor(r*255)).toString(16)+(Math.floor(r*255)).toString(16)+(Math.floor(r*255)).toString(16);
svg_elem.style.fill = col;
setTimeout("changecol()", 50);
}
So colour changes from white to black and back, r*255 keeps it between 00 and FF, everything goes smoothly but when it goes to Black, it flickers to White and starts ascending up basically. Have I missed some error in calculations?
here's a jFiddle with a demo: https://jsfiddle.net/b4f58bz2/
I ran a console.debug on your jsfiddle and I found that the result of (Math.floor(r*255)).toString(16) when it flickers is 'C' in hex, just before '0'. The problem is that when translated to css color it's: #ccc which equals to #cccccc which is a very light color instead of a dark one. You have to pad the result of (Math.floor(r*255)).toString(16) with a leading 0 if its length is less than 2, like:
color = (Math.floor(r*255)).toString(16);
if (color.length < 2) {
color = "0" + color;
}
then just make:
col = '#' + color + color + color;
I hope this gets you on the right track.
To change from black to white you can simply use the Lightness part of HSL with 0% saturation, giving that as color argument:
Replacing setTimeout() with requestAnimationFrame() will also make the animation much smoother as this will sync to the monitor vblank updates. Just compensate by reducing delta to about half.
Example using a div
var l = 5;
var delta = 2.5;
(function changecol() {
if (l < 5 || l > 95) delta = -delta;
l += delta;
d.style.backgroundColor = "hsl(0,0%," + l + "%)";
requestAnimationFrame(changecol);
})();
<div id=d>Ping-pong fade</div>
This took a bit.
The problem is your conversion of toString(16), you aren't considering the possibility that your hex code is too short.
R = hex.length == 1 ? "0" + hex : hex;
Please see:
r = 0.05;
delta = 0.05;
function changecol() {
if (r < 0.05 || r > 0.95) delta = delta * (-1);
r += delta;
hex = (Math.floor(r * 255)).toString(16);
R = hex.length == 1 ? "0" + hex : hex;
col = '#' + R + R + R;
document.getElementById('test').style.backgroundColor = col;
setTimeout("changecol()", 300);
}
document.addEventListener('DOMContentLoaded', changecol, false);
<div id="test">
ADFASDF
</div>
<div id="test1">
ADFASDF
</div>
The other poster already mentioned your bug in encoding small values, so that #0C0C0C for example is encoded as #CCC wich actually represents #CCCCCC.
Besides that your code has some more bad practices in it.
First: use local variables! some may be "needed" global, but col for example has no reason at all, to be made global. That's just environmental pollution.
Next: pass the function to setTimeout, not a string that has to be parsed.
setTimeout(changecol, 50);
It's faster, it's cleaner, it can be encapsulated, it can be minified, ... only benefits.
Then, with a little trick, you can rewrite your color-encoding to be way nicer:
var c = 255 * r;
var col = "#" + (0x1000000 | c << 16 | c << 8 | c).toString(16).substr(1);
the 0x1000000 is padding, so that the values always exceed 6 digits, followed by the three color channels (r, g, b).
This value is converted to a hex-value, having a 1 in front of it from the padding, wich then is removed by the substr(1). Therefore we have always exactly 6 hexadecimal digits, 2 per color-channel, no worrying about leading zeroes and stuff.
And the bit-operations also strip all decimal-places that c might have.
And don't use body-onload to start this thing. If you really have to, use jQuery ($(changecol)) or search for a DOMContentLoaded-implementation, ... or just put the script at the end of the body and know therefore that is executed after all the html is parsed, and the dom is built.
But imo. we can do even better than that. We can make this thing a function of time, instead of incrementing and decrementing the value, and giving it a padding of 5% so you don't go over the borders, ...
var startTime = Date.now();
var speed = 255 / 1000; //255 shades of gray / 1000ms
//this means every 2 seconds this animation completes
//a full cycle from black to white to black again.
//or you slow the speed down to `2*255 / 30000` for example
//1cycle per 30seconds
function changecol(){
//first we calculate the passed time since the start (in ms)
//and then we multiply it by the speed by wich the value should change
//the amount of color-steps per ms
var t = (Date.now() - startTime) * speed;
//now we get the fraction of 256 wich defines the shade
/*
var c = Math.floor(t % 256);
*/
//since we're dealing with powers of 2 we can use a bit-mask to extract just
//the last 8 bit of the value and do basically the same, including the Math.floor()
var c = t & 0xFF;
//every other interval we have to change the direction, so that we fade back from white to black
//and don't start over from 0 to 255 (black to white)
/*
var interval = Math.floor(t / 256); //get the interval
if(interval % 2 === 1) //check wether it's odd
c = 255 - c; //revert the direction
*/
//but again we can do easyer by simply checking the 9th bit.
//if it's a 1, this is an odd interval. the 9th bit equals 0x100 (256).
if(t & 0x100) //mask only the 9th bit. returns either 256 (wich is true) or 0 (false)
c = 0xFF - c; //revert the direction
var col = "#" + (0x1000000 | c << 16 | c << 8 | c).toString(16).substr(1);
test.style.color = col;
//using animation-frames to stay in sync with the rest of animation in the browser.
requestAnimationFrame(changecol);
}
changecol(); //start this thing, don't use body-onload
You could do something like this instead, that way you don't have to try and figure out the correct HEX code.
var whatColor = true;
fadeColor = function() {
var color = whatColor ? 'white' : 'black';
$('svg circle').css({fill: color});
whatColor = !whatColor;
setTimeout(function() {
fadeColor();
}, 1000);
}
fadeColor();
svg circle {
-webkit-transition: fill 1s;
transition: fill 1s;
fill: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<svg class="mySVG" height="100" width="100">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" />
</svg>

increase speed on "bouncing" object

I don't know a lot about JS, but i have to do assignments with it. right now i have a ball that bounces from one side of the screen to the other. with every bounce the colour of the screen and the ball change. but i'd like a slight increase of speed with every bounce as well(or a random speed every time it bounces if that's easier). this is the code I have for moving, the bouncing and the colour changing now:
fill(r,g,b);
ellipse(circleX, circleY, circleSize, circleSize);
circleX += moveX;
if (circleX > width - circleSize / 2 || circleX < circleSize / 2) {
moveX = -moveX;
r = random(255);
g = random(255);
b = random(255);
}
moveX is always 5 now and changes to -5 when turning back. but i'd like it if it turned into -6 and then +7 when going forward again. or something like that at least.
I thank you guys in advance for helping me and please explain it like you're explaining it to a child.
First, lets make a function which takes a number and returns +1 for non-negative numbers (positive or 0) and -1 for negative numbers, i.e. it's sign
function sign(x) {
if (x < 0) return -1;
return 1;
}
A full implementation of sign would have a special case for 0, and is available natively in ES6
Next, when it becomes time to change moveX separate it's magnitude (absolute value) and sign, increment it's magnitude and put the two pieces back together again before flipping the sign over
moveX = -sign(moveX) * (Math.abs(moveX) + 1);
You'll want to add another test inside your collision detection code to increase the speed. If the velocity is positive, then you want to add 1. If the velocity is negative, you want to subtract 1. Your code would look something like this...
...
moveX = -moveX
if (moveX < 0) {
--moveX;
} else {
++moveX;
}
...
Keep track of how many times the circle has "bounced" and add it to the speed.
var base_speed = 5;
var bounces = 0;
var direction = 1; //positive for moving right, negative for moving left
var moveX = base_speed + bounces * direction;
circleX += moveX;
if (circleX > width - circleSize / 2 || circleX < circleSize / 2) {
direction = -direction;
bounces++;
r = random(255);
g = random(255);
b = random(255);
}

Why are my particles drifting? ( aka Is Math.random() broken or is it my algorithm? )

Using Javascript I'm crudely simulating Brownian motion of particles, but for some reason I don't understand my particles are drifting up and to the left.
The algorithm is pretty straight forward. Each particle is a div and I simply add or subtract a random number from each div's top and left position each round.
I read up on Math.random() a little, and I've tried to use a function that returns a random number from min to max inclussive:
// Returns a random integer between min and max
// Using Math.round() will give you a non-uniform distribution!
function ran(min, max)
{
return Math.floor(Math.random() * (max - min + 1)) + min;
}
Here is the function for the movement of the particles:
var x, y, $elie, pos, nowX, nowY, i, $that;
function moveIt()
{
$("div.spec").each(function(i, v) {
x = ran(-5, 5);
y = ran(-5, 5);
$elie = $(v);
pos = $elie.position();
nowX = pos.left;
nowY = pos.top;
// The min and abs are to keep the particles within a box
// The drift occurs even if I remove min and abs
$elie.css("left", Math.min(Math.abs(nowX + x), 515));
$elie.css("top", Math.min(Math.abs(nowY + y), 515));
});
}
And here is how the particles are initially set up an the setInterval started.
$(function() {
$("body").append("<div/>").attr("id","box");
$elie = $("<div/>").attr("class","spec");
// Note that math random is inclussive for 0 and exclussive for Max
for (i = 0; i < 25; ++i)
{
$that = $elie.clone();
$that.css("top", ran(0, 495));
$that.css("left", ran(0, 495));
$("#box").append($that);
}
timer = setInterval(moveIt, 60);
$("input").toggle(function() {
clearInterval(timer);
this.value = " Start ";
}, function() {
timer = setInterval(moveIt, 60);
this.value = " Stop ";
});
});
My problem is that using the min and max from above ( -5, 5 ), all the particles drift up and to the left very fast.
jsFiddle example of drift (-5, 5)
Example of drift even with the removal of .min() and .abs().
To counteract this, I have to use a min and max of -1, 5.
jsFiddle example of no drift (-1, 5)
Here is the CSS for the div all the particles are contained in:
#box {
width:500px;
height:500px;
border:2px #000 solid;
position: relative; }
Here is the default CSS for each particle:
div.spec {
width:5px;
height:5px;
background-color:#00DDAA;
position:absolute; }
What is going on? Why does a min and max of -5 and 5 cause an upward and leftward drift?
A test of the random function ran() doesn't seem to show such a persistent negative drift.
jsFiddle example of testing ran()
The ran() function was taken from the MDC Math.random() page.
Your mistake is to use
pos = $elie.position();
rather than
pos = $elie.offset();
This wouldn't have made a difference had they been added to parent div, but your elements aren't properly added to a parent div, they're appended directly to the document body. So your other mistake is this:
$("body").append("<div/>").attr("id","box");
If you want the div to have id of 'box', the line should read:
$box = $("<div/>").attr("id","box");
$("body").append($box)
Otherwise you're actually giving "body" the id of "box"
EDIT:
The most efficient way to append the div would be the following (as noted by this post):
$(document.createElement('div')).appendTo('body').attr('id', 'box')
Instead of using .position(), try .offset() instead. Looks like it works.
Position.
Offset.
It works this way because you're setting the absolute 'left' and 'top' values in CSS. Instead, you can use this Example:
$elie.css("margin-left", nowX + x);
$elie.css("margin-top", nowY + y);

Categories