文本介绍了采用CSS3的 transform 和 transition 属性来实现下拉菜单的动画效果。
由于css3的动画化和display:block以及display:none这两个属性有所冲突,这里提出了一种解决方法。
这里要实现的动画效果是这样的,首先子菜单用display:none来隐藏,它的初始opacity:0,位置向上有一个偏移
当鼠标滑过目标1的时候,子菜单的属性变为为display:block,之后子菜单边出现在下图中虚线处的位置上。接下来,子菜单的opcacity=1,同时下滑。
html代码如下:
这里subBox初始状态处于隐藏状态,因此设置了一个类hide,让其display:none;
<ul class="menuBox fr">
<li class="liInMenu"><a href="#" class="aInLi"><i class="iconfont bg nav"></i>博客文章</a>
<ul class="subBox hide">
<li><i class="iconfont subNav"></i><a href="#">web前端</a></li>
<li><i class="iconfont subNav"></i><a href="#">nodejs</a></li>
<li><i class="iconfont subNav"></i><a href="#">工具用法</a></li>
</ul>
</li>
<li class="liInMenu"><a href="#" class="aInLi"><i class="iconfont bg nav"></i>兴趣分享</a>
<ul class="subBox hide">
<li><i class="iconfont subNav"></i><a href="#">书籍</a></li>
<li><i class="iconfont subNav"></i><a href="#">影视剧</a></li>
<li><i class="iconfont subNav"></i><a href="#">动漫</a></li>
</ul>
</li>
<li class="liInMenu"><a href="#" class="aInLi"><i class="iconfont bg nav"></i>作品展示</a>
<ul class="subBox hide">
<li><i class="iconfont subNav"></i><a href="#">静态仿站</a></li>
<li><i class="iconfont subNav"></i><a href="#">站点作品</a></li>
<li><i class="iconfont subNav"></i><a href="#">插件</a></li>
</ul>
</li>
<li class="liInMenu"><a href="#" class="aInLi"><i class="iconfont bg nav"></i>关于本站</a></li>
<li class="liInMenu"><a href="#" class="aInLi"><i class="iconfont bg nav"></i>关于我</a></li>
</ul>
liInMenu的css代码如下:
为li设置了relative的定位,以便让subBox基于它定位
.menuBox{
>li{
float: left;
margin-left: 2px;
position: relative;
}
这是subBox的初始状态。
transform:translate3d(0,-50px,0);让其顺着Y轴方向上移。
opacity: 0;让它完全处于透明状态下。
transition:all 0.3s;当subBox要通过css3来实现运动的时候,它所有属性的变化过程都会在持续0.3秒
.subBox{
border: 1px solid #ddd;
border-top: none;
position: absolute;
z-index: 10;
left: 0;
top: 70px;
width: 100%;
background-color: #fff;
opacity: 0;
-webkit-transform:translate3d(0,-50px,0);
-moz-transform:translate3d(0,-50px,0);
-ms-transform:translate3d(0,-50px,0);
-o-transform:translate3d(0,-50px,0);
transform:translate3d(0,-50px,0);
-webkit-transition:all 0.3s;
-moz-transition:all 0.3s;
-ms-transition:all 0.3s;
-o-transition:all 0.3s;
transition:all 0.3s;
}
鼠标滑过liInMenu时,添加类showBox,代码如下:
.showBox{
z-index: 10 !important;
opacity: 1 !important;
-webkit-transform:translate3d(0,0,0) !important;
-moz-transform:translate3d(0,0,0) !important;
-ms-transform:translate3d(0,0,0) !important;
-o-transform:translate3d(0,0,0) !important;
transform:translate3d(0,0,0) !important;
}
关于JS代码,这里用jquery。
最初我打算用事件代理的方式来写,很遗憾,jquery的事件代理不能触发冒泡,而在下拉菜单中必须要用冒泡。
这是原先失败的代码;
//下拉菜单一定要用到冒泡,由事件代理来实现实在是太麻烦了不可取
//由于a和li都要触发下拉菜单,不能这样用
headerMenuBox.on('mouseover',[liInMenuBox,aInLi],function(event){
console.log('enter')
var subMenu = 'undefined';
if ($(event.target).hasClass('liInMenu')) {
subMenu = $(event.target).children('.subBox')
console.log(subMenu)
}
if ($(event.target).hasClass('aInLi')) {
var parent = $(event.target).parent('.liInMenu')
subMenu = parent.children('.subBox')
console.log(subMenu)
}
if (subMenu!=='undefined') {
subMenu.show().addClass('showMenu')
}
})
之后采用了hover来实现,下面我最初的代码
由于需要确定顺序,当移除hide之后再调用函数添加showBox类,确保动画执行。因此用requestAnimationFrame()方法将传入的函数推到下个轮询中调用。
但这里又出现问题了,当我在liInMenu中迅速来回切换的时候,下拉菜单不再显示。
仔细想了想,应该是requestAnimationFrame()的问题,当我从一个liInMenu迅速移动到另一个liInMenu上时,触发了hover的第二个函数,但这时subBox第二个liInMenu的子菜单了,因而没有正确移除目标类。
var subBox = 'undefined'
for (var j = 0; j < liInMenuBox.length; j++) {
liInMenuBox.eq(j).hover(function(event) {
/* Stuff to do when the mouse enters the element */
subBox = $(this).children('.subBox')
subBox.removeClass('hide')
requestAnimationFrame(function(){
subBox.addClass('showBox')
})
}, function() {
/* Stuff to do when the mouse leaves the element */
subBox.removeClass('showBox')//有0.3秒,没有起到作用
requestAnimationFrame(function(){
subBox.addClass('hide')
})
})
}
查明原因后,将获取的每个subBox分别单独赋予liInBox的一个属性上,消除了bug,修改的代码如下。
for (var j = 0; j < liInMenuBox.length; j++) {
liInMenuBox.eq(j).hover(function(event) {
/* Stuff to do when the mouse enters the element */
this.subBox = $(this).children('.subBox')
this.subBox.removeClass('hide')
var self = this
requestAnimationFrame(function(){
self.subBox.addClass('showBox')
})
}, function() {
/* Stuff to do when the mouse leaves the element */
this.subBox.removeClass('showBox')//有0.3秒,没有起到作用
var self = this
requestAnimationFrame(function(){
self.subBox.addClass('hide')
})
//如果用下面的代码还是会出现问题
// setTimeout(function(){
// self.subBox.addClass('hide')
// },350)
})
}
但在从liInBox上移除时,上移的效果没有了,这是因为上移的动作持续了30ms,而requestAnimationFrame()推到下个轮询的间隔估计在30ms以内。
不过这个效果也不错,就没有再修改了。
后续如果解决这个bug我再来更新这片文章。