0

I'm aware of closures, IIFE.
I've read the other answers (they all point to using IIFE).
So why is this not working?

  • my image should gradually fade-in (in 2s)
  • it seems like it's only rendering once (with final value)
var imgFade = document.getElementById('img-fade');
for(i = 0; i < 100; i++){
  (function(step) {
    setTimeout(function() {
      imgFade.style.opacity = (step/100);
    }, 20);
  })(i);
}

here's the code: https://jsfiddle.net/warkentien2/Lh10phuv/1/

EDIT: for future readers
consider all answers transitioning from i = 1; to 1 <= 100, i++
so it won't stop rendering at 99%

warkentien2
  • 969
  • 13
  • 20
  • 2
    Well, you're setting a whole bunch of timeouts practically at once to be executed 20ms later; they're all going to fire extremely rapidly one after the other. You'll have to spread out the timing a bit for it to be *gradual*. – deceze May 30 '16 at 01:04
  • @deceze that's not the issue, or a simple extra 0 on the setTimeout would've fixed it. – warkentien2 May 30 '16 at 01:10
  • 1
    @warkentien2 I think it is the issue, because there's literally nanoseconds between the first `setTimeout` being made and the last one. So all you will see is the last one. `setInterval` is probably what you need. – 4castle May 30 '16 at 01:12
  • 1
    @deceze seems to be right. The settimeout calls dont get called 20ms apart, they all get called at once in ~20ms – anson May 30 '16 at 01:12
  • @deceze well I did say it seems to all be rendering at once. Still don't understand why. (I did bump up the interval to 1000 each. only one render "blink" already 100% opacity) // So you're solution is to create a setInterval at 20ms each, run it 100 times then pause it? How will it update the opacity value? I'm gonna try this. – warkentien2 May 30 '16 at 01:16
  • 1
    Think about it: this loop is going to run in a fraction of a second, setting 100 callbacks to execute ~20ms later. Each callback is also just going to take a fraction of a second, after which the next callback runs. So your execution is 0% opacity, loop, ... 20ms nothing ... 1,2,3,4...100% opacity within a fraction of a second. Visually that change will appear to be instantaneous. You either want to set the timeout as `20 * i` to successively delay each callback more and more, or you need to trigger the next callback 20ms after the previous one has finished. – deceze May 30 '16 at 01:45
  • @deceze now that I know the answer, you're correct. For all calls must happen after one another. It just sounded like that interval was 20ms each, not 20ms before, and a practical at once for all the callbacks. But thanks for helping out! And you're correct to state that tecnically they are not running at the same time (just virtually) – warkentien2 May 30 '16 at 01:52

2 Answers2

4

A quick but dirty way is multiplying the 20 by step. You create all the timeouts at once, so the ones that are supposed to be executed later have higher delay:

var imgFade = document.getElementById('img-fade');
for(i = 0; i < 100; i++){
  (function(step) {
    setTimeout(function() {
      imgFade.style.opacity = (step/100);
    }, step * 20);
  })(i);
}
BoltKey
  • 1,994
  • 1
  • 14
  • 26
  • This is brilliant! Traditionally, people do [`setInterval` like this](http://stackoverflow.com/questions/2956966/javascript-telling-setinterval-to-only-fire-x-amount-of-times), but I like this much better. – 4castle May 30 '16 at 01:19
  • so I was creating all the setTimeouts at once! It took me a while to believe it. I thought it worked as a system('pause'); Thanks! And that not a dirty trick at all. Thanks! – warkentien2 May 30 '16 at 01:19
  • Note that this spams the execution queue with a hundred callbacks at once. While probably not a big problem in practice, it'd be more efficient to schedule the next callback at the end of the previous one. It would also allow you to cancel the fade-in at any time, which gets a bit hard to do when the entire thing is already scheduled to execute with no real way to cancel a hundred callbacks. – deceze May 30 '16 at 01:48
  • @deceze what do you mean callbacks? `setTimeout` has no callback. And, not sure if I'm understanding it right, but still sounds like a loop. Could you send me a function example? – warkentien2 Jun 07 '16 at 02:22
  • @warkentien2 Callback is any piece of code that you pass to a function to be executed when the function ends or after some time. `setTimeout` is a typical example of function with a callback. – BoltKey Jun 07 '16 at 08:16
2

Heres another solution, applying the fade in one after another:

var fade = function(step){
  imgFade.style.opacity = (step/100);
  if(step < 100){
    setTimeout(function(){ fade(++step); }, 20);
  }
};

fade(0);
anson
  • 4,156
  • 2
  • 22
  • 30
  • nice recursion! Now that I get the problem, I can think of a few solutions myself. But I'm giving due credit. – warkentien2 May 30 '16 at 01:23
  • I think a nicer version of this would use `--step` and `if(step > 0)`. That way you can change the number of iterations by changing the initial parameter. – 4castle May 30 '16 at 01:25
  • Sweet, glad you understand how its working! @4castle yes thats an improvement for sure – anson May 30 '16 at 01:31