0. 引言
在 下拉菜单的时候,很常见 鼠标移入,下面的下拉项 缓慢的出来,一个
动画效果
还有 elementUI的选择器组件等其他组件
1. 界面结构搭建
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.item_title {
color: #fff;
background-color: #409eff;
border-color: #409eff;
border-radius: 5px;
;
padding: 5px;
text-align: center;
cursor: pointer;
}
.dropdown-menu {
margin-top: 5px;
background-color: #409eff;
}
.container {
margin: auto;
width: 100px;
}
</style>
</head>
<body>
<div class="container">
<div class="item">
<div class="item_title">下拉菜单</div>
<div class="dropdown-menu">
<div class="dropdown-item">1</div>
<div class="dropdown-item">2</div>
<div class="dropdown-item">3</div>
</div>
</div>
</div>
</body>
</html>
2. 第一种方式:使用 height
最常见的方法是,先让下拉菜单的 盒子
高度变成 0
,然后添加overflow:hidden
,让溢出的元素隐藏,当鼠标移入下拉菜单
这个按钮的时候,把高度变成固定值
.dropdown-menu {
margin-top: 5px;
background-color: #409eff;
height: 0px;
overflow: hidden;
transition: all .5s;
}
.item:hover .dropdown-menu {
height: 100px;
}
但这个方法有一个弊端,有时候高度并
不能直接确定
,谁也不知道里面的内容有多少东西!
那假设,我们把这个 height 属性的值,换成
auto
它就不能实现动画了
因为这个高度是不确定的,浏览器无法对不确定的高度实现动画
.item:hover .dropdown-menu {
height: auto;
}
3. 第二种方式:使用 max-height
可以给
max-height
做动画,只要保证你的内容不会超过这个数就可以
.dropdown-menu {
margin-top: 5px;
background-color: #409eff;
max-height: 0px;
overflow: hidden;
transition: all 1s;
}
.item:hover .dropdown-menu {
max-height: 1000px;
}
这个地方,会导入,
- 鼠标移入的时候,会感觉 很快
- 鼠标移出的时候,会感觉有点卡
因为,鼠标移入的时候,动画过程实际上是
0px => 1000px
又加上移出隐藏了,所以会感觉很快
因为,鼠标移出的时候,动画过程实际上是
1000px => 0px
又加上溢出隐藏了,所以会感觉有一点卡顿
4. 第三种方式:使用 transform
可以使用 transform 的
scaleY
,让它从Y轴
上本来是0
,然后到1
变化
但这个地方,会发现有点奇怪,这个地方,是因为 默认的变换原点就是 DOM 元素的
中心
(垂直水平居中的点
)
可以通过transform-origin: top center;
修改,把 变换原点改成 垂直方向的顶部,水平方向的中心
5. 第五种方式:使用JS
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.item_title {
color: #fff;
background-color: #409eff;
border-color: #409eff;
border-radius: 5px;
padding: 5px;
text-align: center;
cursor: pointer;
}
.dropdown-menu {
margin-top: 5px;
background-color: #409eff;
height: 0;
overflow: hidden;
}
.container {
margin: auto;
width: 100px;
}
</style>
</head>
<body>
<div class="container">
<div class="item">
<div class="item_title">下拉菜单</div>
<div class="dropdown-menu">
<div class="dropdown-item">1</div>
<div class="dropdown-item">2</div>
<div class="dropdown-item">3</div>
</div>
</div>
</div>
<script>
let itemTitleEl = document.querySelector('.item_title')
let dropdownMenuEl = document.querySelector('.dropdown-menu')
itemTitleEl.addEventListener("mouseenter", function () {
dropdownMenuEl.style.height = 'auto'
const { height } = dropdownMenuEl.getBoundingClientRect()
dropdownMenuEl.style.height = 0
console.log("mousenter", height)
dropdownMenuEl.style.transition = 'all 1s'
dropdownMenuEl.style.height = height + 'px'
})
itemTitleEl.addEventListener("mouseleave", function () {
dropdownMenuEl.style.height = 0
})
</script>
</body>
</html>
这个实现思路就是:
- 监听鼠标移入
下拉菜单按钮(document.querySelector('.item_title'))
事件 - 鼠标移入后,把
下拉菜单(document.querySelector('.dropdown-menu'))
height 换成auto
,目的是为了拿到这个下拉菜单DOM的高度
- 使用 DOM.getBoundingClientRect() ,拿到这个
下拉菜单DOM的高度
- 给
下拉菜单
高度设置成 拿到的实际值 - 给
下拉菜单
添加transition
- 鼠标移出,高度变成
0
可以看到鼠标移入以后是 直接闪一下的,根本没有做动画,这个原因是因为:先设置了 高度是
auto
,然后设置高度为0
,然后又设置高度为具体值
,,,修改高度,会触发回流(Layout)
,
但是浏览器为了避免,连续多次操作导致
布局树反复计算
,浏览器会合并
这些操作,当JS代码全部完成后在进行统一计算
,所以,回流 这个操作,导致 布局树发生计算是异步
的
所以如若想让它立即执行 回流,则需要 获取
布局属性
,让浏览器立即执行回流,因为获取布局属性相关的信息,需要计算,所以可以触发浏览器立即执行回流
比如以下属性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight
这里可以采用
DOM.getBoundingClientRect()
也会立即触发回流,强制浏览器重新计算布局,确保它检测到高度变化
最终代码
<script>
let itemTitleEl = document.querySelector('.item_title')
let dropdownMenuEl = document.querySelector('.dropdown-menu')
itemTitleEl.addEventListener("mouseenter", function () {
dropdownMenuEl.style.height = 'auto'
const { height } = dropdownMenuEl.getBoundingClientRect()
dropdownMenuEl.style.height = 0
console.log("mousenter", height)
dropdownMenuEl.getBoundingClientRect() // 强制浏览器重新计算布局,确保它检测到高度变化
dropdownMenuEl.style.transition = 'all 1s'
dropdownMenuEl.style.height = height + 'px'
})
itemTitleEl.addEventListener("mouseleave", function () {
dropdownMenuEl.style.height = 0
})
</script>