Number is not incrementing by 1 in JavaScript - javascript

I have this code where if the opacity is less than or equal to 0, the message number is suppose to go up by 1, but when I run the code, the message number increases by 77 or 152 or 66, etc. Could you help me?
My code:
//variables
var x = 0;
var opacity = 0;
var messageNumber = 0;
var talk1 = ["hello", "welcome to idle ball", "potato"];
var lol = 1;
//set opacity to 1
function opacitySet1(speed) {
document.getElementById("talk").style.opacity = opacity;
opacity += speed;
}
//set opacity to 0
function opacitySet0(speed) {
document.getElementById("talk").style.opacity = opacity;
opacity -= speed;
}
function IntervalManager(flag, animate, time, para1) {
if (flag) {
var intervalSet = setTimeout(animate, time, para1)
}
}
function IntervalManagerII(flag, animate, time, para1) {
if (flag) {
var intervalSetII = setTimeout(animate, time, para1)
}
}
//to delay time
function nothing() {}
function message(startPart) {
document.getElementById("talk").innerHTML = messageNumber;
if (opacity >= 0 && lol == 0) {
setTimeout(nothing, 1);
IntervalManagerII(true, opacitySet0, 300, 0.005);
IntervalManager(false)
}
if (opacity <= 1 && lol == 1) {
IntervalManager(true, opacitySet1, 300, 0.005);
IntervalManagerII(false)
}
if (opacity <= 0) {
lol = 1;
IntervalManagerII(false);
messageNumber += 1;
} //this is the part that is not working
if (opacity >= 1) {
lol = 0;
IntervalManager(false);
}
};
setInterval(function() {
message(0)
});

New answer
After discussing in the comments, it turns out you think JavaScript timers are blocking the execution of the main thread. It does not work this way. Consider the following example (2 is printed almost instantly, and 1 is printed after one second).
> | setTimeout(function(){console.log(1)}, 1000);
| setTimeout(function(){console.log(2)}, 0);
< | ...
| 2
| 1
Also read this article from jQuery's creator.
Since your code is based on a wrong assumption, I think it makes no sense to work on your question any longer.
Old answer
Your setInterval timer is running a lot faster than your setTimeout timers, meaning that it queues a lot of setTimeout timers before starting to increment the opacity. During this time, the message is incremented and printed at interval max speed. After a couple of ms, all setTimeout timers start firing one after the other with almost no delay between them, and interleaving with setInterval timers, which leads to an (almost) unpredictable mess.

Related

Increment value in time

I am looking to increment the value of "time" with 0.01 each 10 miliseconds until it gets to the desired value. Right now it just increases it instantly to the conditioned value.
var time = 0;
function animate() {
decreaseIncrement = -0.78;
increaseIncrement = 0.78;
if (
(document.getElementById("but5").onclick = function () {
if (time < increaseIncrement) {
do {
time += 0.01;
} while (time < increaseIncrement);
}
})
)
if (
(document.getElementById("but3").onclick = function () {
if (decreaseIncrement < time) {
do {
time -= 0.01;
} while (decreaseIncrement < time);
}
})
)
increaseIncrement = time + increaseIncrement;
decreaseIncrement = time + decreaseIncrement;
}
https://jsfiddle.net/2epqg1wc/1/
You can solve that problem using setInterval which repeatedly runs a task every x milliseconds until you cancel it. Below code reduces the value to 0 in 0.01 steps with a step performed every 10 milliseconds.
var value = 1.0;
var decrement = 0.01;
function decreaseAnimation() {
var interval = setInterval(() => {
value -= decrement;
console.log(value);
if (value <= 0) {
clearInterval(interval);
}
}, 10);
}
decreaseAnimation();
You have 3 options:
requestAnimationFrame (rAF)
setTimeout/setInterval (sTo)
messageChannel
The first 2 options are more straightforward but they will lack the precision, because rAF fires every 17 milliseconds (assuming 60Hz) and sTO will fire at most 4ms after 4 successive recursions. Usually rAF is preferred over sTo because of better reliability in timing of firing these callbacks. Use sTO as a fallback if rAF is not supported.
Here is an implementation from a library for similar purposes:
var rafx = require("rafx");
rafx.async({ //create a ledger object to store values
curr_time:0,
desired:Math.random(),
frames:0
}).animate(function(obj){
//obj is the ledger above
//increment obj.frames here if you want to
return obj;
},).until(function(obj){
obj.frames++;
obj.curr_time = obj.frames * 17 / 10 * 0.01;
return obj.curr_time >= obj.desired;
}).then(function(obj){
console.log("sequence ended with values:" + JSON.stringify(obj));
});
You can copy paste the code above here and test it.
The last option uses MessageChannel to post message between ports, which gives extremely high precision because it is fired at the next event loop. You can combine this with performance.now to determine whether to increment your time or not.
Disclosure: I am the author of the aforementioned lib.

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!

countdown from n to 0 in given time, negative end value

I am working on simple script that should animate given value (for example 6345.23) to 0 by counting it down, it should also end up at 0 if specified amount of time have passed (for example 2 seconds.
I started by simple logic:
given config: initial value, time in sec, interval
time is given in seconds so convert it to milliseconds
calculate amount of ticks by dividing time in ms by interval
calculate amount of decreased value per tick by dividing initial value by amount of ticks
once above are known we can simply do: (simple model, not actual code)
intId = setInterval(function() {
if(ticks_made === amount_of_ticks) {
clearInterval(intId);
} else {
value -= amount_per_tick;
// update view
}
}, interval);
actual code:
var value = 212.45,
time = 2, // in seconds
interval = 20; // in milliseconds
var time_to_ms = time * 1000,
amount_of_ticks = time_to_ms / interval,
amount_per_tick = (value / amount_of_ticks).toFixed(5);
var start_time = new Date();
var ticks_made = 0;
var intId = setInterval(function() {
if(ticks_made === amount_of_ticks) {
console.log('start time', start_time);
console.log('end time', new Date());
console.log('total ticks: ', amount_of_ticks, 'decresed by tick: ', amount_per_tick);
clearInterval(intId);
} else {
value = (value - amount_per_tick).toFixed(5);
console.log('running', ticks_made, value);
}
ticks_made++;
}, interval);
Link do fiddle (in console you can observe how it works)
If you set time to 2 (2 seconds) its ok, but if you set time to for example 2.55 (2.55 seconds) it doesnt stop at all at 0, its passing by and going indefinitely in negative values.
How i can fix it so no matter what is set in seconds its always go precisly one by one until reaches perfectly 0?
var value = 212.45,
time = 2, // in seconds
interval = 20; // in milliseconds
var time_to_ms = time * 1000,
amount_of_ticks = time_to_ms / interval,
amount_per_tick = (value / amount_of_ticks).toFixed(5);
var start_time = new Date();
var ticks_made = 0;
var intId = setInterval(function() {
if(ticks_made === amount_of_ticks) {
console.log('start time', start_time);
console.log('end time', new Date());
console.log('total ticks: ', amount_of_ticks, 'decresed by tick: ', amount_per_tick);
clearInterval(intId);
} else {
value = (value - amount_per_tick).toFixed(5);
console.log('running', ticks_made, value);
}
ticks_made++;
}, interval);
You're relying on ticks_made === amount_of_ticks being an exact match. Chances are, due to rounding, you won't get an exact match, so you'd be better off doing:
if(ticks_made >= amount_of_ticks) {
kshetline's answer correctly addresses why you get into negative values. When dealing with fractional IEEE-754 double-precision binary numbers (in the normal range, or even whole numbers in very high ranges), == and === can be problematic (for instance, 0.1 + 0.2 == 0.3 is false). Dealing with values as small as the fractional values here are, accumulated imprecision is also a factor. It's inevitable to have to fudge the final step.
But there's a larger issue: You can't rely on timers firing on a precise schedule. Many, many things can prevent their doing so — other UI rendering work, other scripts, CPU load, the tab being inactive, etc.
Instead, the fundamental technique for animation on browsers is:
Update when you can
Update based on where you should be in the animation based on time, not based on how many times you've animated
Use requestAnimationFrame so your update synchronizes with the browser's refresh
Here's your code updated to do that, see comments:
// Tell in-snippet console to keep all lines (rather than limiting to 50)
console.config({maxEntries: Infinity});
var value = 212.45,
time = 2.55, // in seconds
time_in_ms = time * 1000,
amount_per_ms = value / time_in_ms,
interval = 100 / 6, // in milliseconds, ~16.66ms is a better fit for browser's natural refresh than 20ms
ticks_made = 0;
// A precise way to get relative milliseconds timings
var now = typeof performance !== "undefined" && performance.now
? performance.now.bind(performance)
: Date.now.bind(Date);
// Remember when we started
var started = now();
// Because of the delay between the interval timer and requestAnimationFrame,
// we need to flag when we're done
var done = false;
// Use the interval to request rendering on the next frame
var intId = setInterval(function() {
requestAnimationFrame(render);
}, interval);
// About half-way in, an artificial 200ms delay outside your control interrupts things
setTimeout(function() {
console.log("************DELAY************");
var stop = now() + 200;
while (now() < stop) {
// Busy-loop, preventing anything else from happening
}
}, time_in_ms / 2);
// Our "render" function (okay, so we just call console.log in this example, but
// in your real code you'd be doing a DOM update)
function render() {
if (done) {
return;
}
++ticks_made;
var elapsed = now() - started;
if (elapsed >= time_in_ms) {
console.log(ticks_made, "done");
done = true;
clearInterval(intId);
} else {
var current_value = value - (amount_per_ms * elapsed);
console.log(ticks_made, current_value);
}
}
/* Maximize in-snippet console */
.as-console-wrapper {
max-height: 100% !important;
}
If you run that, then scroll up to the "************DELAY************" line, you'll see that even though rendering was held up by "another process", we continue with the appropriate next value to render.
It would make sense to convert the result of .toFixed() to a number right away:
let amount_per_tick = +(value / amount_of_ticks).toFixed(5);
let value = +(value - amount_per_tick).toFixed(5);
(note the + signs)
Then you will never have to worry about type coercion or anything, and instead just focus on math.

Function is called more than once

I am creating a game similar to Pacman. The game board is held in an array called "testLevel." Here, I am trying to code the ghosts and make them move one square per 5 second. What happens is that every 5 seconds the ghost function will be called, but the program runs so fast that the function gets called multiple times within that second when I only want it to run once then not run again until another 5 seconds. How can I fix this problem. Thanks!
var testLevel = [[0,0,0,0,0,0],[0,1,0,1,1,0],[0,0,1,0,1,0],[0,0,1,0,1,0],[0,1,4,1,1,0],[0,0,0,0,0,0]];
function draw() {
background(255);
var sec = second();
if (sec % 5 == 0) {
ghost();
}
}
function ghost(){
for(b=1; b <7 ;b++){// column
for (a=5; a>-1; a--){// row
if (testLevel[a][b] == 4 && testLevel [a-1][b] !== 0){
c = a;
d = b;
printBoard();
}
}
}
testLevel[c][d] =1;
testLevel[c-1][d] = 4;
}
It sounds to me like you want to use some sort of timing function, either
setTimeout(function, milliseconds)
---Executes a function, after waiting a specified number of milliseconds.
or
setInterval(function, milliseconds)
---Same as setTimeout(), but repeats the execution of the function continuously.
(From http://www.w3schools.com/js/js_timing.asp)
In this case, setInterval(ghost, 5000) in draw() should do the trick.
Instead of looping to determine 5 seconds, use setInterval:
var testLevel = [[0,0,0,0,0,0],[0,1,0,1,1,0],[0,0,1,0,1,0],[0,0,1,0,1,0],[0,1,4,1,1,0],[0,0,0,0,0,0]];
function draw() {
background(255);
var interval = setInterval(ghost, 5000)
function ghost(){
for(b=1; b <7 ;b++){// column
for (a=5; a>-1; a--){// row
if (testLevel[a][b] == 4 && testLevel [a-1][b] !== 0){
c = a;
d = b;
printBoard();
}
}
}
testLevel[c][d] =1;
testLevel[c-1][d] = 4;
}
Note: You can use clearInterval(interval) to stop the process.

Javascript not fading in

I'm trying to create a fading effect using vanilla.js instead of jQuery. I'm using the following code to create the hide and show effect:
document.getElementById("pic-num-submit").onclick = function() {
fade();
};
var home = document.getElementById('home').style;
home.opacity = 1;
var agree = document.getElementById('agree').style;
agree.opacity = 0;
agree.display = "none";
function fade() {
if((home.opacity -= .1) < 0) {
home.display = "none";
show();
}
else {
setTimeout(fade, 40);
}
}
function show() {
if((agree.opacity += 0.2) < 1) {
agree.display = "";
}
else {
setTimeout(show, 40);
}
}
Everything is working with the fade function (in Firefox) But when I invoke the show function it only runs once, then it removes the display styling, and shows the div element at 0.2 opacity. I'm not sure what I'm doing wrong.
Here is a jsFiddle example of the problem I am having: http://jsfiddle.net/L54Aw/2/
Also it's broken in Chrome for some reason (The fade function never completes because of something to do with a js decimal subtracting problem)
Your "show" function is not correct. You only set up the timer when the opacity is not less than one. Initially, it is, so the code only runs once.
You're also running into a weirdness in JavaScript that pertains to a significant difference between the + and - operators. Subtraction is always numeric, but not so addition!
Here's a working "show" function:
function show() {
agree.display = ""; // only need this the first time anyway
agree.opacity = +(agree.opacity) + 0.2;
if (agree.opacity <= 1)
setTimeout(show, 40);
}
That unary + operator forces the "opacity" property to be interpreted as a number. Without that, it's a string! Thus adding 0.2 to the string "0.2" gives you "0.20.2", which is nonsense.
The decrementing you did for the other element worked OK because the subtraction operator coerces operands to numbers.
I can't comment on Pointy's solution, but here's what you could do with the fade function to avoid the bug that happens when you subtract low numbers:
function fade() {
home.opacity -= .05;
if(home.opacity - .05 < 0) {
home.display = "none";
show();
}
else {
setTimeout(fade, 20);
}
}
It isn't bulletproof, but it works by subtracting .05 instead of .1 and counter it by doubling the speed of the animation.
you probably want
(agree.opacity += 0.2) > 1
instead of
(agree.opacity += 0.2) < 1

Categories