根据传统,我们先修复一下上一章的bug。

有两个bug:

  1. 当同步动画和队列动画同时存在的时候,队列动画会触发多次。
  2. 如果同步动画中,两个值或者多个值相差太多就无法完整运行。

原因:

主要原因是因为我们在定时器中使用了for语句,然后是同步动画的时候,假设是两个动画要同时运行,那么就会触发两个if判断,然后这两个if判断到最后都会运行一次setAttr()或者setOpacity()的方法,而这两个的方法里面都有if(obj.fn != undefined) obj.fn(); 这个语句,fn本身就是队列动画,那么就会导致触发多次。

而无法完成运行是因为,假设宽度要到300,高度101,元素本身高度是100,那么这样设置,高度会先到达目标点,从而触发setAttr()方法,这个方法里面有clearInterval(element.timer);清除超时调用,这样就导致超时调用中的宽度还在递增的时候就被强制停止了。

解决办法:

我们可以不让这个setAttr()或者setOpacity()的方法运行fn和清除超时调用。

先在超时调用里面创建一个判断元素,判断动画是否已经完成,如果完成,我们再运行fn和清理超时调用。

于是:

 element.timer = setInterval(function() {
     var flage = true;
     for (var i in mul) {
         attr = i == 'x' ? 'left' : i == 'y' ? 'top' :
             i == 'w' ? 'width' : i == 'h' ? 'height' :
             i == 'o' ? 'opacity' : i != undefined ? i : 'left';
         end = mul[i];

         if (type == 'buffer') {
             step = attr == 'opacity' ? (end - getOpacity()) / speed :
                 (end - parseInt(getStyle(element, attr))) / speed;
             step = (step > 0) ? Math.ceil(step) : Math.floor(step);
         }
         if (attr == 'opacity') {
             if (step == 0) {
                 setOpacity();
             } else if (step > 0 && Math.abs(getOpacity() - end) <= step) {
                 setOpacity();
             } else if (step < 0 && (getOpacity() - end) <= Math.abs(step)) {
                 setOpacity();
             } else {
                 var temp = getOpacity();
                 element.style.opacity = (temp + step) / 100;
                 element.style.filter = 'alpha(opacity=' + (temp + step) + ')'
             }
             if (end != getOpacity()) flage = false;
         } else {
             if (step == 0) {
                 setAttr();
             } else if (step > 0 && Math.abs(parseInt(getStyle(element, attr)) - end) <= step) {
                 setAttr();
             } else if (step < 0 && (parseInt(getStyle(element, attr)) - end) <= Math.abs(step)) {
                 setAttr();
             } else {
                 element.style[attr] = parseInt(getStyle(element, attr)) + step + 'px';
             }
             if (end != parseInt(getStyle(element, attr))) flage = false;
         };
     };
     if (flage) {
         clearInterval(element.timer);
         if (obj.fn != undefined) obj.fn();
     }

 }, time);

 function setAttrsetAttr() {
     element.style[attr] = end + 'px';
 }

 function setOpacity() {
     element.style.opacity = parseInt(end) / 100;
     element.style.filter = 'alpha(opacity=' + parseInt(end) + ')';
 }
 function getOpacity() {
      if (system.ie <= 8) {
          var op = getStyle(element, 'filter');
          var s = [];
          if (op.match(/opacity=([\d]+)/)) {
              s = op.match(/\=([\d]+)/);
          } else {
              throw new Error('ie8一下没有获取到filter中的opacity值');
          }
          return parseInt(s[1]);
      } else {
          return parseFloat(getStyle(element, 'opacity')) * 100;
      }
}

先创建一个flage元素,默认为true,然后将setAttr()和setOpacity()方法中运行fn和清理超时调用删除。

在opcacity和attr的if判断中,在他们if运算完后,再来判断end值是否不等于当前元素即时获取的值,如果不等于让flage等于false。

当for(var i in mul)语句都循环完毕后,说明第一次已经运行完毕了,加上我们并没有清理超时调用,这时候便会再次运行,我们在这个for语句后面判断,判断flage是否为true的时候,如果是true,说明当前元素的值已经等于end了,那么就运行fn和清理超时调用。

这里就解决了fn多次调用的问题,实际上连同步动画相差太大的问题也解决了。

这里就有疑问了,同步动画的时候,for语句会循环,那么应该是同时都会触发 if (end != parseInt(getStyle(element, attr))) flage = false;这个判断的,按照js代码的先后顺序,如果前一个判断为false,后一个为true,那么后一个的值应该会覆盖前一个的。

但是实际上并不是这样的,for语句循环的两个因为处理速度快,基本上算是同时运行,这里我们假设宽度要到300,高度101,元素本身高度是100,高度的if判断一下子就完成了,因为他只要+1,而宽度300要经过三次if判断,这里高度就会先于宽度判断完flage的值,然后宽度再判断,再重新赋值给flage。

这里就会有快慢之分,慢的flage赋值永远是最后的,这样只有慢的那个也完成了,才会触发清理超时调用和运行fn。

于是完美解决上面上个问题。

展示菜单:

给个人设置添加动画效果:

调用代码:

//个人设置
$('#header .nav-confin').hover(function() {
    $(this).css('background-color', '#2b2c2e');
    $('#header .nav-menu').animation({
        'mul': {
            'o': 100,
            'h': 120
        }
    });
}, function() {
    $(this).css('background-color', 'transparent');
    $('#header .nav-menu').animation({
        'mul': {
            'o': 0,
            'h': 0
        }
    });
});
$('#header .menu-li').hover(function() {
    $(this).css('background-color', '#2b2c2e');
}, function() {
    $(this).css('background-color', 'transparent');
});

直接使用同步动画,不用再设置css的height了。

效果图:

分类: JavaScript 标签: 展示菜单

评论

全部评论 2

  1. 11
    11
    Google Chrome Windows 10
    11
  2. 1
    1
    QQ Browser Android Oreo
    4

目录