JavaScript: Opacity should reduce to 0 and then become negative - javascript

I have a javaScript function.
JavaScript:
function opacZero(object){
var presentOpacity = (object.style.opacity);
object.style.opacity =presentOpacity-0.2;
setTimeout( function(){opacZero(object)}, 40);
}
Now when I pass som eobject to this function, its opacity should reduce till 0 and go beyond 0 (as I am not clearing the timeout anywhere). But this is not happening. The opacity reduces to 0.20000000000000007 and reduces no more. But when I subtract 0.3 (or more) instead of 0.2, it is giving the desired result. But why not with numbers less than 0.2. I have no idea why this is happening. Help PLease

This is due to how Javascript handles floating point numbers. Check out this SO question for some suggestions on how to work around it.
EDIT
Here's a way to work around it:
function opacZero(object){
var presentOpacity = Math.floor(object.style.opacity * 10);
object.style.opacity = (presentOpacity - 2) / 10;
setTimeout( function(){opacZero(object)}, 40);
}

This is a possible work around:
function opacZero(object){
curOpac = curOpac < 0.1 ? 0 : curOpac - 0.2;
object.style.opacity = curOpac;
setTimeout( function(){
opacZero(object)
}, 400);
console.log( curOpac );
}
var test = document.getElementById( 'test' ),
curOpac = 1;
test.style.opacity = 1;
opacZero( test );
http://jsfiddle.net/daCrosby/bhTNC/1/

Seems a little more straightforward to just check for the troublesome small values and force those to 0. Also probably a good idea to stop the timer once you hit 0:
function opacZero(object){
var newOpacity = object.style.opacity - 0.2;
if (newOpacity < 0.2) {
newOpacity = 0;
}
object.style.opacity = newOpacity;
if (newOpacity !== 0) {
setTimeout(function(){opacZero(object)}, 40);
}
}
Working demo: http://jsfiddle.net/jfriend00/8qkFN/
Or, if you want something briefer:
function opacZero(object){
object.style.opacity = object.style.opacity < 0.4 ? 0 : object.style.opacity - 0.2;
if (object.style.opacity != 0) {
setTimeout(function(){opacZero(object)}, 40);
}
}

Related

IF condition is always false in this case

I am trying to reduce opacity with JavaScript. But the problem is that 'if' condition is always false therefor the opacity is getting 0. Please can anyone explain why this is happening and the correct solution.
var opacity = 1;
var decrement = 0.01;
var id = setInterval(frame, 10);
function frame(){
if (opacity == 0.4) //It is always false..
{
clearInterval(id);//Not able to clear Interval
}
else
{
opacity = opacity-decrement;
document.getElementsByClassName('menu_bar')[0].style.backgroundColor='rgba(66,64,61,'+opacity+')';
}
}
if(Math.abs(opacity - 0.4) < 0.01) {
Floating point math is "broken", therefore you have to be fault tolerant.
Instead of using opacity == 0.4 in your code, try using opacity < 0.5 or opacity <= 0.4 as fractions are not very reliable in javascript.
Try running 1-0.1-0.1-0.1-0.1-0.1-0.1 (The math that happens before your == 0.4 check should return true) in your console to see what I mean.
As mentioned, the floating point math is broke.
A simple fix is to use integers and divide the end result with 100.
That will also avoid any extra Math.abs, or calc, at every interval.
Stack snippet
var opacity = 100;
var decrement = 1;
var id = setInterval(frame, 10);
function frame() {
if (opacity == 40)
{
clearInterval(id);
} else {
opacity = opacity - decrement;
//document.getElementsByClassName('menu_bar')[0].style.backgroundColor='rgba(66,64,61,'+opacity/100+')';
document.body.style.backgroundColor='rgba(66,64,61,'+opacity/100+')';
}
}
You can use opacity.toFixed(2) to restrict the decimal digits to 2, this will sole the issue
var opacity = 1;
var decrement = 0.01;
var id = setInterval(frame, 10);
function frame(){
if (opacity.toFixed(2) == 0.40) //It is always false..
{
console.log("Cleared");
clearInterval(id);//Not able to clear Interval
}
else
{
opacity = opacity-decrement;
// document.getElementsByClassName('menu_bar')[0].style.backgroundColor='rgba(66,64,61,'+opacity+')';
}
}

Adding easing on requestAnimationFrame

I need to reproduce the same effect as here: http://www.chanel.com/fr_FR/mode/haute-couture.html = a swipe effect on mouse move event.
I just need some help on the animation part.
function frame() {
$('.images-gallery').css({
'transform': 'translateX('+ -mouseXPerc +'%)'
});
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
$(document).on('mousemove',function(e){
mouseXPerc = e.pageX/containerWidth*100;
});
Here's what I've done so far. It works as supposed, but as you can imagine, it's pretty raw, I need some easing in that. How can I edit my frame() function to get something smoother ?
Edit : I can't use CSS transition / animation as I change the value on requestAnimationFrame (each 1/30 sec).
I think I've found an answer for you. It's based on this library
First, I would just grab a function from that site
function inOutQuad(n){
n *= 2;
if (n < 1) return 0.5 * n * n;
return - 0.5 * (--n * (n - 2) - 1);
};
Then, I would use a modified form of the example code, something like this
function startAnimation(domEl){
var stop = false;
// animating x (margin-left) from 20 to 300, for example
var startx = 20;
var destx = 300;
var duration = 1000;
var start = null;
var end = null;
function startAnim(timeStamp) {
start = timeStamp;
end = start + duration;
draw(timeStamp);
}
function draw(now) {
if (stop) return;
if (now - start >= duration) stop = true;
var p = (now - start) / duration;
val = inOutQuad(p);
var x = startx + (destx - startx) * val;
$(domEl).css('margin-left', `${x}px`);
requestAnimationFrame(draw);
}
requestAnimationFrame(startAnim);
}
I might change how 'stop' is calculated, I might write something to ensure that it ends on destx, etc, but that's the basic format
Showing it in this jsfiddle
I'm actually kinda proud of this one. I've been wanting to figure this out for a while. Glad I had a reason to.
You can create your own ease function and use it inside your frame function:
var ease = function() {
var x = 0;
return function(x_new) {
x = (x_new+x)*.5;
return x;
}
}();
function frame() {
$('.images-gallery').css({
'transform': 'translateX('+ -ease(mouseXPerc) +'%)'
});
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
$(document).on('mousemove',function(e){
mouseXPerc = e.pageX/containerWidth*100;
});

Incrementing a for loop by decimal value

I'm trying to implement a for loop that increments by 0.1. I have one that seems to work just fine for an increment of 0.5. Although this may be a 'false positive' since the one for 0.1 gives me some strange values for i?
function thisisi() {
var x = 1;
for (var i = (x+0.1); i < 2; i += 0.1) {
console.log('i', i);
}
};
Seems to yield:
i 1.1
i 1.2000000000000002
i 1.3000000000000003
i 1.4000000000000004
i 1.5000000000000004
i 1.6000000000000005
i 1.7000000000000006
i 1.8000000000000007
i 1.9000000000000008
Instead of what I need which is 1.1, 1.2, 1.3 etc.
Can someone please point out the root of my idiocy?
You can use .toFixed() to limit the number of digits appear after the decimal point:
function thisisi() {
var x = 1;
for (var i = (x+0.1); i < 2; i += 0.1) {
console.log('i', i.toFixed(1));
}
};
Fiddle Demo
Just as "one third" (1/3) cannot be expressed precisely in decimal (0.333...) then one-tenth (0.1) cannot be expressed precisely in binary (it's 0.0001100110011...).
Try this :
function thisisi(){
var x = 1;
for (var i = x; i < 2; i += 0.1) {
var str = Math.floor( i * 1000 ) / 1000;
console.log(str);
}
};
thisisi();

setInterval response only once instead of 1000

I want to change the opacity of table with setInterval:
window.setInterval(function () {
if (document.getElementById("goalkeepers").style.opacity != 1)
document.getElementById("goalkeepers").style.opacity = document.getElementById("goalkeepers").style.opacity + 0.001;
}, 1);
When I run this code it do it only once, the opacity of the table is only 0.001.
What is the problem?
There are several things wrong with the code itself:
DRY, you are calling getElementById to get the same element a LOT.
You are assuming that 0.001 + 0.001 + 0.001 + ... a thousand times will add up to exactly 1. This is not true in computers, because of how floating point numbers work. You should use < 1 instead of != 1
Retrieving a style is always a string. Therefore the result is trying to be 0.0010.001, ie. concatenation.
You are using an interval of 1ms. Intervals should generally not be less than about 20.
Try:
var tbl = document.getElementById('goalkeepers');
tbl.style.opacity = 0;
tbl.style.transition = "opacity 1s linear";
setTimeout(function() {
// delay the actual opacity setting so it will be transitioned
tbl.style.opacity = 1;
},10);
These guys beat me to it while I was making a fiddle, But I will post anyway.
var gk = document.getElementById("goalkeepers"),
animation = window.setInterval(function () {
if (gk.style.opacity < 1) {
gk.style.opacity = Number(gk.style.opacity) + 0.001;
}
else {
clearInterval(animation);
}
}, 1);
They both are correct about using < istead of != for the float type comparison, the string value of style.xxx and only calling document.getElementById once.
You should consider excepting one of their answers.
Try this
var t= window.setInterval(function () {
if (parseInt(document.getElementById("goalkeepers").style.opacity) != 1)
{
document.getElementById("goalkeepers").style.opacity = Number(document.getElementById("goalkeepers").style.opacity) + 0.001;
}
else
{
clearInterval(t);
}
}, 1);
You opacity is not incrementing, so used Number() also you have clear the interval once opacity reached 1 else it will keep calling might slow down your browser after some time.

How to make a JavaScript function that usually returns true, but returns false randomly, once every 50 times on average?

I'd like to make a function that returns true most of the time when it's called but that will return false occasionally?
By occasionally I mean, at 1000 calls, it'll have an average of one false every 50 calls.
Pretty easy, use Math.random to get a number between 0 and 1, and compare it to 0.02 or the rate of true/false you are looking for.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
The Math.random() function returns a floating-point, pseudo-random number in the range [0, 1) that is, from 0 (inclusive) up to but not including 1 (exclusive), which you can then scale to your desired range.
So, basically:
var randomizer = function () {
return Math.random() > 0.02;
};
EDIT
If you want to make it more beautiful, you can pack this function in an object, and set there the ratio of true/false.
var randomizer = {
_ratio: 0.02, // default ratio
setRatio: function (falses, total) {
this._ratio = falses / total;
},
getResult: function () {
return Math.random() > this._ratio;
}
};
and then
randomizer.getResult();
randomizer.setRatio(60, 1000); // 60 out of 10000
function mostlyFalse() {
return Math.random() <= 0.05;
}
jsfiddle randomly showing mostly 'true's, but with the occasional 'false': http://jsfiddle.net/t8E6t/1/
How about
Odds: 50/1000 = 1/20
//Get random number from 1 to 20, if equals 20 return false
if (Math.floor((Math.random()*20)+1) == 20)
{
return false;
}
else{
return true;
}
With Math.random, you can achieve what you want :
function suchRandom(chanceInPercent){
return Math.random() > chanceInPercent/100;
}
console.log(suchRandom(2)); //because 1000/50 = 20 true / 1000 calls = 2%
Around 20 : http://jsfiddle.net/Ru7qY/1/
function foo () { return Math.random() > 0.02; }
// 1 / 50 == 0.02, so if it's less than 0.02 return false
Create a random number each time the function runs, between 1 and 50:
var num = Math.floor((Math.random()*50)+1);
Then, if the number equals 50, for example, return false.
I feel like the most concise, simple answer is this one-liner function
// A function that has a 50% chance of returning true by default
function chance( c = 0.5 ){
return Math.random() < c;
}
Use it like so
// Block has a 60% chance of being executed.
if( chance( .6 ) ){
//Do stuff
}
// Or, to solve your problem specifically
var c = chance( 49 / 50 );
counter.... :D
var c = 0;
function f()
{
return ++c % 50 == 0 ? false : true;
}

Categories

Resources