展开收起效果是比较常见的一种交互方式,一般的作法是控制display
属性值在none
和其它值之间切换,虽然说功能能够实现,可是效果略显生硬,因此会有这样的需求——但愿元素展开收起能具备平滑的效果。javascript
首先想到的是经过height
在0
与auto
之间切换,可是结果可能并不会是咱们所预期的那样,缘由是咱们将要展开的元素内容是动态的,即高度值不肯定,所以height
使用的值是默认的auto
,从0px
到auto
是没法计算的,所以没法造成过渡或动画效果。
据此咱们可使用max-height
,将max-height
从0
过渡到一个可以大于彻底显示内部元素的值,展开后的max-height值,只须要设定为保证比展开内容高度大的值便可,在max-height
值比height
值大的状况下,元素仍会默认采用自身的高度值即auto
,如此一来一个高度不定的元素展开收起动画效果就实现了。
请注意这种方式实现仍是有限制的,使用CSS
进行过渡动画的时候依旧是经过计算0
到设定的max-height
高度值进行计算的,在实际应用中若是max-height
值太大,在元素收起的时候将会产生延迟的效果,这是由于在收起时,max-height
从设定的特别大的值,到元素自身高度值的变化过程将占用较多时间,此时画面表现则会产生延迟的效果。所以建议将max-height
值设置为足够安全的最小值,这样在收起时即便有略微延迟,也会由于时间很短,难以被用户感知,将不会影响体验。css
<!DOCTYPE html> <html> <head> <title>展开动画</title> <style type="text/css"> .page{ width: 200px; padding: 10px 20px; border: 1px solid #eee; } .container { overflow: hidden; } .container > .options{ transition: all .5s; max-height: 0; } .container > .unfold{ max-height: 150px; } .container > .btn{ color: #4C98F7; cursor: pointer; text-decoration: underline; } </style> </head> <body> <div class="page"> <div class="container"> <div class="btn" onclick="operate(this)" unfold="1">展开</div> <div class="options"> <div class="option">选项1</div> <div class="option">选项2</div> <div class="option">选项3</div> <div class="option">选项4</div> <div class="option">选项5</div> <div class="option">选项6</div> <div class="option">选项7</div> </div> </div> </div> </body> <script type="text/javascript"> function operate(btn){ const optionsNode = document.querySelector(".container > .options"); const unfold = btn.getAttribute("unfold"); if(unfold && unfold==="1"){ btn.innerText = "收缩"; optionsNode.classList.add("unfold"); }else{ btn.innerText = "展开"; optionsNode.classList.remove("unfold"); } btn.setAttribute("unfold", unfold === "0" ? "1" : "0"); } </script> </html>
使用max-height
一定有必定的局限性,那么不如咱们在DOM
加载完成后就取得元素的实际高度并保存,以后直接利用这个真实高度与0
进行动画过渡便可,由于浏览器的渲染顺序,在解析JavaScript
时会阻塞DOM
的渲染,因此在获取元素实际高度再设置高度为0
的过程当中通常不会出现闪烁的状况,若是实在担忧由于获取高度以后再将高度设置为0
可能会有一个闪烁的过程,那么咱们能够取得元素父节点后调用cloneNode(true)
方法或者innerHTML
方法取得字符串再innerHTML
到一个新建立的节点,目的就是将其拷贝,以后将其使用绝对定位等放置到屏幕外即将其设置到屏幕可以显示的外部区域,注意此时要设置body
的overflow: hidden;
,以后利用getComputedStyle
取得实际高度,而后再将其移出DOM
结构,此时有了实际高度就能够进行动画过渡了,下面简单的实现一下在DOM
加载时便取得实际高度进行动画实现。html
<!DOCTYPE html> <html> <head> <title>展开动画</title> <style type="text/css"> .page{ width: 200px; padding: 10px 20px; border: 1px solid #eee; } .container { overflow: hidden; } .container > .options{ transition: all .5s; } .container > .btn{ color: #4C98F7; cursor: pointer; text-decoration: underline; } </style> </head> <body> <div class="page"> <div class="container"> <div class="btn" onclick="operate(this)" unfold="1">展开</div> <div class="options"> <div class="option">选项1</div> <div class="option">选项2</div> <div class="option">选项3</div> <div class="option">选项4</div> <div class="option">选项5</div> <div class="option">选项6</div> <div class="option">选项7</div> </div> </div> </div> </body> <script type="text/javascript"> (function init(win, doc){ const optionsNode = document.querySelector(".container > .options"); optionsNode.setAttribute("real-height", win.getComputedStyle(optionsNode).height); optionsNode.style.height = "0px"; })(window, document); function operate(btn){ const optionsNode = document.querySelector(".container > .options"); const unfold = btn.getAttribute("unfold"); const realHeight = optionsNode.getAttribute("real-height"); if(unfold && unfold==="1"){ btn.innerText = "收缩"; optionsNode.style.height = realHeight; }else{ btn.innerText = "展开"; optionsNode.style.height = "0px"; } btn.setAttribute("unfold", unfold === "0" ? "1" : "0"); } </script> </html>
还有一种经常使用实现动画的方式,即首先将外层元素没有动画过渡的形式直接展开,再将选项加入动画缓慢下落,一般利用transform: translateY();
去实现这个缓慢降低的动画,在微信的WEUI
小程序组件库的首页就是采用这种实现方式。java
<!DOCTYPE html> <html> <head> <title>展开动画</title> <style type="text/css"> .page{ width: 200px; padding: 10px 20px; border: 1px solid #eee; } .container, .options-container { overflow: hidden; } .options-container{ height: 0; } .container .options{ transition: all .5s; transform: translateY(-100%); } .container .unfold{ transform: translateY(0); } .container > .btn{ color: #4C98F7; cursor: pointer; text-decoration: underline; } </style> </head> <body> <div class="page"> <div class="container"> <div class="btn" onclick="operate(this)" unfold="1">展开</div> <div class="options-container"> <div class="options"> <div class="option">选项1</div> <div class="option">选项2</div> <div class="option">选项3</div> <div class="option">选项4</div> <div class="option">选项5</div> <div class="option">选项6</div> <div class="option">选项7</div> </div> </div> </div> </div> </body> <script type="text/javascript"> function operate(btn){ const optionsNode = document.querySelector(".container .options"); const optionsContainer = document.querySelector(".options-container"); const unfold = btn.getAttribute("unfold"); if(unfold && unfold==="1"){ btn.innerText = "收缩"; optionsNode.classList.add("unfold"); optionsContainer.style.height = "auto"; }else{ btn.innerText = "展开"; optionsNode.classList.remove("unfold"); optionsContainer.style.height = "0px"; } btn.setAttribute("unfold", unfold === "0" ? "1" : "0"); } </script> </html>
https://github.com/WindrunnerMax/EveryDay
http://www.111com.net/wy/192615.htm https://zhuanlan.zhihu.com/p/52582555 https://cloud.tencent.com/developer/article/1499033