一、概念
flip动画分为4个阶段:
- first(记录要监控的元素位置)
- last(记录元素结构变化的位置)
- inver(移动元素到first的位置-“归位”)
- play(播放动画)
flip就是4个阶段的首字母,它的特点是动画是在元素变动完成后,再开始播放动画,此时的元素已经是位于终点了,所以播放的过渡动画是“归位”的过渡动画
二、例子
过渡动画:鼠标移入按钮时,wrapper高度为auto,移出按钮时,高度设置为0
想要使用css实现这种动画,有几种方式:
- 由于过渡动画要求动画属性值必须是数值,height的属性为auto和0时,无法看到动画效果,那么设置一个max-height。但是max-height为了一定要显示完整的wrapper内容,往往需要设置地很大,这样动画就会有一定的加快和延迟,以为在单位时间内,假使动画是匀速的,那么在移入时动画会非常快,而在移出时,动画会有一定的延时
- 使用缩放属性,缺点在于会压缩内容,如果产品和ui觉得可以,这个方式也可
- 使用网格布局,grid-template-rows的值是数值类型的,过渡动画没问题,但是在safari浏览器不支持
使用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>
.wrapper {
width: 200px;
background: deeppink;
height: 0;
overflow: hidden;
transition: 0.3s;
}
</style>
</head>
<body>
<button>按钮</button>
<div class="wrapper">
<p>文字</p>
<p>文字</p>
<p>文字</p>
<p>文字</p>
<p>文字</p>
<p>文字</p>
<p>文字</p>
<p>文字</p>
</div>
<script>
const btn = document.querySelector('button')
const wrapper = document.querySelector('.wrapper')
// flip思想:先让元素到达最终状态,获取到最终状态的height,再让元素回到初始状态,再播放动画,从初始状态变更为最终状态
btn.onmouseenter = function () {
wrapper.style.height = 'auto' // 先设置为auto,目的是为了拿到元素实际的高度
const { height } = wrapper.getBoundingClientRect()
wrapper.style.height = 0 // 再设置为0,隐藏wrapper元素。这样页面会不会闪一下?不会,在执行js的过程中,浏览器是来不绘制的
wrapper.offsetHeight // 触发页面重排,强制渲染height为0
wrapper.style.height = height + 'px'
}
btn.onmouseleave = function () {
wrapper.style.height = 0
}
</script>
</body>
</html>
问题1:为什么在设置height为0和设置height为height + 'px'中间读取offsetHeight?
这涉及到浏览器的重排原理,当获取元素布局属性时,会立即重排。我读取offsetHeight的目的是必须要让浏览器先展示height为0的样式,然后再设置height为实际高度,此时transition的过渡时间才会生效
问题2:为什么获取布局属性时会引起重排?
当你访问元素的布局属性时,浏览器必须要给你一个正确的属性值,例如访问offsetHeight,浏览器会立即暂停并应用所有样式和布局改动,返回准确的offsetHeight值
这里如果获取的是btn的offsetHeight属性也是一样的会引起重排,或者是获取其他布局属性(scrollWidth)都是会一样地引起重排
如果你不知道获取元素布局属性会导致页面重排,那么使用定时器也是可以实现一样的效果:
setTimeout(() => {
wrapper.style.height = height + 'px'
}, 100)
如果你觉得这篇文章对你有用,可以看看作者封装的库xtt-utils,里面封装了非常实用的js方法。如果你也是vue开发者,那更好了,除了常用的api,还有大量的基于element-ui组件库二次封装的使用方法和自定义指令等,帮你提升开发效率。不定期更新,欢迎交流~