jquery风格的fadeIn()和fadeOut()

之前发的一篇fadeIn和fadeOut的代码,发现在手机上有点问题。今天发现这篇还支持ie浏览器的,作者的分析很多,看代码更舒服。

FadeOut()

Let’s start with the fadeOut() function. We’ll pass in 2 arguments: the duration and the element to be removed, the duration being ‘ms’ for milliseconds, and ‘el’ shortened from ‘element’.

function fadeOut(ms, el) {

}

To progressively reduce the element opacity, we will need to use a timer. Every ‘X’ milliseconds, this timer will call a function which reduces the opacity smoothly.

function fadeOut(ms, el) {
  var fading = window.setInterval(func, interval);
}

Beware, ‘interval’ isn’t the duration. The interval is the duration between two calls. It will be an arbitrary defined value, small enough to make the animation as smooth as possible, but not too much to avoid being too heavy. Let’s say 50ms.

If you divide the interval by the duration, you get the amount of opacity which is taken off at each function call. So if you want the animation to be 1000ms long, and the interval is 50ms long, you’ll have 20 calls (1000/50) each one reducing opacity by 0.05 (50/1000). Let’s start by defining our variables;

function fadeOut(ms, el) {
  var opacity = 1,
    interval = 50,
    gap = interval / ms;
    
  var fading = window.setInterval(func, interval);
}

Now, we have to define the function that will actually decrease opacity. Since this won’t be called by anything else than our timer, we can define it in our fadeOut() function:

function fadeOut(ms, el) {
  var opacity = 1,
    interval = 50,
    gap = interval / ms;
    
    function func() {  }
    
    var fading = window.setInterval(func, interval);
}

I’ve introduced a new function called func(), which decreases the element opacity by ‘gap’.

function fadeOut(ms, el) {
  var opacity = 1,
    interval = 50,
    gap = interval / ms;
    
  function func() { 
    opacity -= gap;
    el.style.opacity = opacity;
  }
  
  var fading = window.setInterval(func, interval);

}

Almost done! We have to make it stop when it reaches 0 so that it doesn’t keep running, sucking resources and slowing down the whole page. We make a quick if statement to check the opacity level is less than or equal to zero, and if so, we clearInterval.

function fadeOut(ms, el) {
  var opacity = 1,
    interval = 50,
    gap = interval / ms;
    
  function func() {
    opacity -= gap;
    el.style.opacity = opacity;
    
    if(opacity <= 0) {
      window.clearInterval(fading);
    }
  }
  
  var fading = window.setInterval(func, interval);
  
}

Last but not least, we have to remove the item from the flow (which is different from removing the item from the DOM) by setting it to ‘display:none;’.

function fadeOut(ms, el) {
  var opacity = 1,
    interval = 50,
    gap = interval / ms;
    
  function func() { 
    opacity -= gap;
    el.style.opacity = opacity;
    
    if(opacity <= 0) {
      window.clearInterval(fading); 
      el.style.display = 'none';
    }
  }
  
  var fading = window.setInterval(func, interval);

}

Our function is finished. Here is how we can use it:

var el = document.getElementById('myElement');
el.onclick = function(e) {
  fadeOut(750, this);
}

FadeIn()

The fadeIn() is pretty much the same thing so I want explain it as I did for the previous one. I’ll just drop the code and let you figure out what’s going on. 😉

function fadeOut(ms, el) {
  var opacity = 0,
    interval = 50,
    gap = interval / ms;
    
  el.style.display = 'block';
  el.style.opacity = opacity;
  
  function func() { 
    opacity += gap;
    el.style.opacity = opacity;
    
    if(opacity >= 1) {
      window.clearInterval(fading);
    }
  }
  
  var fading = window.setInterval(func, interval);
  
}

Note the major difference: before doing anything, we display the element to block and set its opacity to 0 so that we see the difference when the opacity gets increased.

Merging both functions

The fact is: both functions look pretty much the same. For the sake of brevity, we could merge both functions into a single one, accepting a new argument; the type of animation,in or out. This will make things much more efficient and manageable. Let’s start with this:

function fade(type, ms, el) {

  function func() {
  
  }
  
  var fading = window.setInterval(func, interval);

}

Now, a few things depend on if the transition is in or out, like the default opacity, the incrementation/decrementation, the display applied, and so on. Let’s start with getting the type. Since there are more things going on when it’s a fadeIn, I created a boolean depending on if the type is in or not in.

function fade(type, ms, el) {
  var isIn = type === 'in',
    opacity = isIn ? 0 : 1,
    interval = 50,
    gap = interval / duration;
    
  ...
  
}

The other variables do not change except the opacity which is equals to 0 if it’s a fadeIn() and 1 if it’s a fadeOut(). ‘opacity = isIn ? 0 : 1’ is the shorthand for:

if(isIn === true) {
  opacity = 0;
} else {
  opacity = 1;
}

Do you remember we have to set the display to ‘block’ and the opacity to 0 if it’s a fadeIn() animation? Let’s do it now:

function fade(type, ms, el) {
  var isIn = type === 'in',
    opacity = isIn ? 0 : 1,
    interval = 50,
    gap = interval / duration;
  
  if(isIn) {
    el.style.display = 'block';
    el.style.opacity = opacity;
  }
  
  ...
  
}

Now the animation internal function. It decrements ‘opacity’ by ‘gap’ if it’s a fadeOut() (‘opacity -= gap’) and it increments it if it’s a fadeIn() (‘opacity += gap’).

function fade(type, ms, el) {

  ...
  
  function func() {
    opacity = isIn ? opacity + gap : opacity - gap;
    el.style.opacity = opacity;
  }
  
  ...

}

We still have two things left: if the opacity is below 0, we set the element to ‘display:none;’ and if the opacity is below 0 or beyond 1, we clear the timer to stop the animation.

function fade(type, ms, el) {
  var isIn = type === 'in',
    opacity = isIn ? 0 : 1,
    interval = 50,
    gap = interval / duration;
    
  if(isIn) {
    el.style.display = 'block';
    el.style.opacity = opacity;
  }
  
  function func() {
    opacity = isIn ? opacity + gap : opacity - gap;
    el.style.opacity = opacity;
    
    if(opacity <= 0) el.style.display = 'none'
    if(opacity <= 0 || opacity >= 1) window.clearInterval(fading);
  }
  
  var fading = window.setInterval(func, interval);
  
}

Voila! We have a single function handling both fadeIn() and fadeOut().

var el = document.getElementById('my-element');
el.onclick = function(e) {
  fade('out', 500, this);
}
document.onclick = function(e) {
  fade('in', 1000, el);
}

What about Internet Explorer 8?

When doing ‘el.style.opacity’, it changes the value of the ‘opacity’ property on the element ‘el’. Problem is Internet Explorer 8 and below don’t understand this property. If we want to enable support for these browsers, we will need to use Microsoft proprietary filters.

/* For Internet Explorer 5, 6 and 7 */
el.style.filter = 'alpha(opacity=' + opacity + ')';

/* For Internet Explorer 8 */
el.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(Opacity=' + opacity + ')';

So everytime we change opacity, we have to use this. Not great huh? What about passing a flag to the function so that you can force IE support only when you really need it? Let’s try it.

function fade(type, ms, el, IE) {
  var isIn = type === 'in',
    opacity = isIn ? 0 : 1,
    interval = 50,
    gap = interval / duration;
        
    if(isIn) {
         el.style.display = 'block';
         el.style.opacity = opacity;
         if(IE) {
            el.style.filter = 'alpha(opacity=' + opacity + ')';
            el.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(Opacity=' + opacity + ')';
        }
    }
    
    function func() {
        opacity = isIn ? opacity + gap : opacity - gap; 
        el.style.opacity = opacity;
        if(IE) {
            el.style.filter = 'alpha(opacity=' + opacity * 100 + ')';
            el.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(Opacity=' + opacity * 100 + ')';
        }
        
        if(opacity <= 0 || opacity >= 1) window.clearInterval(fading);
        if(opacity <= 0) el.style.display = 'none';
    }
    
    var fading = window.setInterval(func, interval);
}

Usage

Here is how you can use the function – which is also in the demo:

//fadeIn
fade('in', this, 750, true);

//fadeOut
fade('out', this, 750, true);

querySelector

It’s worth pointing out that you don’t need to use an ID attribute to use raw JavaScript, querySelector is almost as great as jQuery’s selector. If you’re supporting IE8 and above, then it’s much better to use it (demo uses getElementById), like so:

var classname = document.querySelector('.classname');

That’s pretty much it guys! Please, be sure to ask any question or even propose something better. 😉


关注我

我的微信公众号:前端开发博客,在后台回复以下关键字可以获取资源。

  • 回复「小抄」,领取Vue、JavaScript 和 WebComponent 小抄 PDF
  • 回复「Vue脑图」获取 Vue 相关脑图
  • 回复「思维图」获取 JavaScript 相关思维图
  • 回复「简历」获取简历制作建议
  • 回复「简历模板」获取精选的简历模板
  • 回复「加群」进入500人前端精英群
  • 回复「电子书」下载我整理的大量前端资源,含面试、Vue实战项目、CSS和JavaScript电子书等。
  • 回复「知识点」下载高清JavaScript知识点图谱

每日分享有用的前端开发知识,加我微信:caibaojian89 交流