JS 展示菜单
根据传统,我们先修复一下上一章的bug。
有两个bug:
- 当同步动画和队列动画同时存在的时候,队列动画会触发多次。
- 如果同步动画中,两个值或者多个值相差太多就无法完整运行。
原因:
主要原因是因为我们在定时器中使用了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了。
效果图:
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
全部评论 2
11
Google Chrome Windows 101
QQ Browser Android Oreo