CSS魔法堂:Transition就這么好玩


前言

 以前說起前端動畫必須使用JS,而CSS3為我們帶來transition和@keyframes,讓我們可以以更簡單(聲明式代替命令式)和更高效的方式實現UI狀態間的補間動畫。本文為近期對Transition的學習總結,歡迎各位拍磚。

屬性介紹

 首先先我們簡單粗暴了解transition屬性吧!

transition: <transition-property> <transition-duration> <transition-timing-function> <transition-delay>;

/* 設置啟用Transition效果的CSS屬性
 * 注意:僅會引發repaint或reflow的屬性可啟用Transition效果
 *       [CSS_animated_properties](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties)
 */
<transition-property>: all | none | <property> [,<property>]*

/* 設置過渡動畫持續時間,單位為s或ms
 */
<transition-duration>: 0s | <time> [, <time>]*

/* 設置過渡動畫的緩動函數
 * cubic-bezier的值從0到1
 * [一個很好用的cubic-bezier配置工具](http://cubic-bezier.com)
 */
<transition-timing-function>: linear|ease|ease-in|ease-out|ease-in-out|cubic-bezier(n,n,n,n)

/* 設置過渡動畫的延時,單位為s或ms
 */
<transition-delay>: 0s | <time> [, <time>]

 另外我們可以一次性為多個CSS屬性啟動Transition效果

transition: width 1s ease .6s,
            color .5s linear,
            background 2s ease-in-out;

觸發方式

 既然Transition是UI狀態間的補間動畫,那么有且僅有修改UI狀態時才能讓動畫動起來。那么就有3種方式了:

  1. 偽類.:link,:visited,:hover,:active:focus
  2. 通過JS修改CSS屬性值
  3. 通過JS修改className值

TransitionEnd事件詳解

TransitionEnd Event

el.addEventListener("transitionend"
  , e => 
    {
      const pseudoElement = e.pseudoElement // 觸發動畫的偽類
          , propertyName = e.propertyName   // 發生動畫的CSS屬性
          , elapsedTime = e.elapsedTime     // 動畫的持續時間
      // ..................
    })

注意:每個啟用TransitionCSS屬性的分別對應獨立的transitionend事件

/* 觸發3個transitionend事件 */
transition: width 1s ease .6s,
            color .5s linear,
            background 2s ease-in-out;

Visibility也能transition?

 在可啟用Transition的CSS屬性中,我們發現到一個很特別的CSS屬性——visibilityvisibility常與display相提並論的屬性,它憑什么能啟用Transition,而display不行呢?這個我真心不清楚,不過我們還是了解啟用transition的visibility先吧!
visibility是離散值,0(hidden)表示隱藏,1(visible)表示完全顯示,非0表示顯示。那么visibility狀態變化就存在兩個方向的差異了:

  1. 隱藏顯示,由於非0就是顯示,那么從值從0到1的過程中,實際上是從隱藏直接切換到顯示的狀態,因此並沒有所謂的變化過程;
  2. 顯示隱藏,從1到0的過程中,存在一段時間保持在顯示的狀態,然后最后一瞬間切換到隱藏,因此效果上是變化延遲,依然沒有變化過程。

 上述表明啟用transition的visibility並沒有補間動畫的視覺效果,那么到底有什么作用呢?答案就是不影響/輔助其他CSS屬性的補間動畫。其中最明顯的例子就是輔助opacity屬性實現隱藏顯示的補間動畫。

<style>
.form-input{
	display: inline-flex;
	line-height: 2;
	border: solid 1px rgba(0,0,0,0.3);
}
.form-input:hover{
		border: solid 1px rgba(0,0,0,0.4);
}
.form-addon{
	font-style: normal;
	color: #666;
	background: #ddd;
	padding-left: 10px;
	padding-right: 10px;
	border-right: solid 1px rgba(0,0,0,0.3);
}
.form-addon-after{
	border-left: solid 1px rgba(0,0,0,0.3);
	border-right: none 0;
}
.form-control{
	border:none 0;
	outline-color: transparent;
	padding: 5px;
	caret-color: #888;
	font-size: 16px;
}
.tips-host{
	position: relative;
}
.tips{
	cursor: default;
	z-index: 999;
	position: absolute;
	top: 120%;
	font-size: 12px;
	color: #444;
	background: #ccc;
	padding: .5em;
	box-shadow: 2px 2px 10px #999;
	transition: box-shadow .2s,
		               opacity 1s,
		               visibility .8s;
	visibility: hidden;
	opacity: 0;
}
.tips:hover{
	box-shadow: 2px 2px 5px #999;
}
.tips::before{
	content: "";
	border: solid 10px transparent;
	border-bottom: solid 10px #ccc;
	position:  absolute;
	transform: translate(0, -100%);
}
.form-control:focus + .tips{
	visibility: visible;
	opacity: 1;
}
</style>
<div class="form-input tips-host" >
	<i class="form-addon">Amount</i>
	<input class="form-control">
	<div class="tips">
		Starts with alphabet, then anything you would like.
	</div>
</div>

 當opacity:0時,需要元素隱藏了但實際上它仍然位於原來的位置,而且可以攔截和響應鼠標事件,當出現元素重疊時則會導致底層元素失效。而由於visibility:hidden時,元素不顯示且不攔截鼠標事件,所以在補間動畫的最后設置visibility:hidden為不俗的解決辦法。

display:none讓transition失效的補救措施

 雖然修改display有可能會引發reflow,但它依然不能啟用Transition,這點真心要問問委員會了。更讓人疑惑的是,它不單不支持啟用Transition,而且當設置display:none時其余CSS屬性的Transition均失效。難到這是讓元素脫離渲染樹的后果??

<style>
.box{
  display: none;
  background: red;
  height: 20px;
}
</style>
<div class="box"></div>
<button id="btn1">Transition has no effect</button>
<button id="btn2">Transition takes effect</button>
<script>
const box = document.querySelector(".box")
    , btn1 = document.querySelector("#btn1")
    , btn2 = document.querySelector("#btn2")
btn1.addEventListener("click", e => {
  box.style.display = "block"
  box.style.background = "blue"
})
btn2.addEventListener("click", e => {
  box.style.display = "block"
  box.offsetWidth              // 強制執行reflow
  box.style.background = "blue"
})
</script>

上面的代碼,當我們點擊btn1時背景色的transition失效,而點擊btn2則生效,關鍵區別就是通過box.offsetWidth強制執行reflow,讓元素先加入渲染樹進行渲染,然后再修改背景色執行repaint。
那么我們可以得到的補救措施就是——強制執行reflow,下面的操作均可強制執行reflow(注意:會影響性能哦!)

offsetWidth, offsetHeight, offsetTop, offsetLeft
scrollWidth, scrollHeight, scrollTop, scrollLeft
clientWidth, clientHeight, clientTop, clientLeft
getComputeStyle(), currentStyle()

總結

尊重原創,轉載請注明轉自:https://www.cnblogs.com/fsjohnhuang/p/9143035.html _肥仔John

參考

小tip: transition與visibility
https://www.cnblogs.com/surfaces/p/4324044.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM