成人怡红院-成人怡红院视频在线观看-成人影视大全-成人影院203nnxyz-美女毛片在线看-美女免费黄

站長資訊網(wǎng)
最全最豐富的資訊網(wǎng)站

你了解 Transition 嗎?一起來深入了解下Transition!

你了解 Transition 嗎?你可能并不了解 Transition?下面本篇文章就來通過圖文結(jié)合的方式帶大家深入了解一下Transition,希望對大家有所幫助!

你了解 Transition 嗎?一起來深入了解下Transition!

這篇文章我們深入學(xué)習(xí) Transition 動畫。沒錯,CSS3 Transition 動畫。你可能會問,不是很簡單嗎,這什么好講的?

確實,Transition 動畫使用起來非常容易。只需要給元素加上 transition-delay, transition-duration, transition-property, transition-timing-function 屬性就可以有過濾效果。更簡單的用法是直接使用簡寫的 transition 屬性:

transition: <property> <duration> <timing-function> <delay>;  // transition-delay 默認(rèn)為 0 // transition-property 默認(rèn)為 all // transition-timing-function 默認(rèn)為 ease transition: 0.3s;

由于 transition 動畫用起來幾乎沒有成本,一直以來也沒有太深入學(xué)習(xí),最近翻看源代碼和 MDN 文檔之后發(fā)現(xiàn)有些知識沒有理解到位,于是乎有了這篇文章,希望對讀者更深入了解 Transition 動畫有所幫助。(學(xué)習(xí)視頻分享:css視頻教程)

為了盡量降低閱讀理解成本,這篇文章會寫得稍微啰嗦一點點,大部分示例都會配圖 ——【多圖預(yù)警開始!】

什么是 Transition?

簡單的說就是過渡動畫,通常修改 DOM 節(jié)點的樣式都是立即更新在頁面上的,例如修改寬高,修改透明度,修改背景色等等。

例如當(dāng)鼠標(biāo)移動至按鈕上時,為了突出按鈕的可交互,會在 hover 時修改它的樣式,讓用戶注意到它。沒有加 transition 過渡動畫,給用戶的感覺會很僵很生硬。

.button {   // ...   background-color: #00a8ff; }  .button:hover {   background-color: #fbc531;   transform: scale(1.2); }

你了解 Transition 嗎?一起來深入了解下Transition!

加上 transition 一行代碼之后,變化就會比較順滑。

.button {   // ...   transition: 1s; } // ...

這個例子中我們修改了 background-colortransform,結(jié)合 transition 屬性,瀏覽器就會自動讓屬性值隨著時間變化,從舊值逐步過渡到過渡新值,視覺上就是動畫效果。

你了解 Transition 嗎?一起來深入了解下Transition!

區(qū)分于 AnimationTransition 動畫側(cè)重于表現(xiàn)一次過渡效果,從開始到結(jié)束的變化。而 Animation 不需要變化,可以循環(huán)播放 ▶️。

需要注意,并不是所有的屬性變化都會有過渡效果

  1. 有些 CSS 屬性只支持枚舉值,非黑即白,不存在中間狀態(tài),例如 visibility: visible; 被修改成 visibility: hidden; 不會有動畫效果,因為不存在可見又不可見的中間狀態(tài)。在瀏覽器上的表現(xiàn)是 duration 到了之后元素立即突變?yōu)?hidden。

    .button:hover {   //...   visibility: hidden; }

    你了解 Transition 嗎?一起來深入了解下Transition!

  2. 有些屬性雖然是可計算數(shù)值,但天生注定不能有過渡效果,例如 transition-delaytransition-duration 都是立即生效,這里值得補(bǔ)一句由于 transition-* 屬性是即時生效,這行代碼如果是 hover 時才加上,那么效果會是 hover 時有動畫,移出時沒有動畫。
  3. 即使是可過渡的屬性變化,也可能因為無法計算中間狀態(tài)而失去過渡效果。例如 box-shadow 屬性雖然支持 transition 的動畫的,但如果從 "outset" 切換到 inset,也是突變的。

    .button {   // ...   box-shadow: 0 0 0 1px rgb(0 0 0 / 15%);   transition: 1s; }  .button:hover {   // ...   box-shadow: inset 0 0 0 10px rgb(0 0 0 / 15%); }

    你了解 Transition 嗎?一起來深入了解下Transition!
    從表現(xiàn)上看,box-shadow 的變化是 hover 上去立馬就生效了。

  4. 如果某個屬性值是連續(xù)可計算的數(shù)值,但是變化前后變成散列的枚舉值,那么過渡也不會生效。例如從 height: 100px => height: auto 是不會有動畫的。

以上的內(nèi)容回顧了 Transition 的基本用法,下面我們來看一個在實際開發(fā)場景中會遇到的問題。

為什么 Transition 動畫沒有生效?

場景題:假設(shè)我們現(xiàn)在接到一個自定義下拉選擇器的動畫需求,設(shè)計師給到的效果圖如下:

你了解 Transition 嗎?一起來深入了解下Transition!

這是很常見的出現(xiàn)-消失動畫,在很多組件庫里面都會出現(xiàn),點擊觸發(fā)器(按鈕)時才在頁面上渲染 Popup (下拉內(nèi)容),并且 Popup 出現(xiàn)的同時需要有漸現(xiàn)和下滑的動畫;展開之后再次點擊按鈕,Popup 需要漸隱和上滑。

平時使用的時候并沒有過多注意它的實現(xiàn),不妨現(xiàn)在讓我們動手試驗一下。

暫時忽略 popup 的內(nèi)容,用了個 div 來占位模擬,HTML 結(jié)構(gòu)很簡單。

<div class="wrapper">     <div id="button"></div>     <div id="popup"></div> </div>

在點擊按鈕的時候,讓 popup 顯示/隱藏,然后切換 popup.active 類名。

const btn = document.querySelector("#button"); const popup = document.querySelector("#popup");  if (!popup.classList.contains("active")) {     popup.style.display = "block";     popup.classList.add("active"); } else {     popup.style.display = "none";     popup.classList.remove("active"); }

編寫 CSS 樣式,在不 active 時透明度設(shè)置為 0,向上偏移,active 時則不偏移且透明度設(shè)置為 1。

#popup {   display: none;   opacity: 0;   transform: translateY(-8px);   transition: 1s;    &.active {     opacity: 1;     transform: translateY(0%);   } }

完整代碼 在這里,看起來代碼沒什么問題,點擊按鈕切換的時候,popup 應(yīng)該會有動畫過渡效果。然而實際運行效果:

你了解 Transition 嗎?一起來深入了解下Transition!

硬邦邦地完全沒有過渡效果,這是為啥?明明已經(jīng)設(shè)置了 transition,且 opacitytranslateY 都是可計算可過渡的數(shù)值,也產(chǎn)生了變化,瀏覽器為什么不認(rèn)呢?

在查文檔之前,我們先嘗試使用萬精油 setTimeout

方案一:setTimeout 萬精油

修改 JS 代碼:

btn.addEventListener("click", () => {   if (!popup.classList.contains("active")) {     popup.style.display = "block";     setTimeout(() => {       popup.classList.add("active");     }, 0);   } else {     popup.classList.remove("active");     setTimeout(() => {       popup.style.display = "none";     }, 600);   } });

可以看到添加了 setTimeout 之后,transition 動畫就生效了。

你了解 Transition 嗎?一起來深入了解下Transition!

隱藏時的 setTimeout 600ms 對應(yīng) CSS 中設(shè)置的 transition: 0.6s,就是動畫完成之后才將 display 設(shè)置為 none

主要困惑的點在于為什么顯示的時候也需要加 setTimeout 呢?setTimeout 0 在這里起到的作用是什么?帶著問題去翻看規(guī)范文檔。

在規(guī)范文檔的 Starting of transitions 章節(jié)找到下面這段話:

When a style change event occurs, implementations must start transitions based on the computed values that changed in that event. If an element is not in the document during that style change event or was not in the document during the previous style change event, then transitions are not started for that element in that style change event.

翻譯一下,當(dāng)樣式變更事件發(fā)生時,實現(xiàn)(瀏覽器)必須根據(jù)變更的屬性執(zhí)行過渡動畫。但如果樣式變更事件發(fā)生時或上一次樣式變更事件期間,元素不在文檔中,則不會為該元素啟動過渡動畫。

結(jié)合瀏覽器構(gòu)建 RenderTree 的過程,我們可以很清晰地定位到問題:當(dāng)樣式變更時間發(fā)生時,display: none 的 DOM 元素并不會出現(xiàn)在 RenderTree 中(style.display='block' 不是同步生效的,要在下一次渲染的時候才會更新到 Render Tree),不滿足 Starting of transitions 的條件。

你了解 Transition 嗎?一起來深入了解下Transition!

所以 setTimeout 0 的作用是喚起一次 MacroTask,等到 EventLoop 執(zhí)行回調(diào)函數(shù)時,瀏覽器已經(jīng)完成了一次渲染,再加上 .active 類名,就有了執(zhí)行過渡動畫的充分條件。

優(yōu)化方案二:精準(zhǔn)卡位 requestAnimationFrame

既然目的為了讓元素先出現(xiàn)到 RenderTree 中,和渲染相關(guān),很容易想到可以將 setTimeout 替換成 requestAnimationFrame,這樣會更精準(zhǔn),因為 requestAnimation 執(zhí)行時機(jī)和渲染有關(guān)。

if (!popup.classList.contains("active")) {     popup.style.display = "block";      requestAnimationFrame(() => {         popup.classList.add("active");     }); }

補(bǔ)充一個小插曲:在查找資料的過程中了解到 requestAnimationFrame 的規(guī)范是要求其回調(diào)函數(shù)在 Style/Layout 等階段之前執(zhí)行,起初 Chrome 和 Firefox 是遵循規(guī)范來實現(xiàn)的。而 Safari 和 Edge 是在執(zhí)行的時機(jī)是在之后。 從現(xiàn)在的表現(xiàn)上來看,Chrome 和 Firefox 也改成了在之后執(zhí)行,翻看以前的文檔會說需要嵌套兩層 requestAnimationFrame,現(xiàn)在已經(jīng)不需要了。Is requestAnimationFrame called at the right point?

優(yōu)化方案三:Force Reflow

在規(guī)范文檔中,還留意到以下這句話:

Implementations typically have a style change event to correspond with their desired screen refresh rate, and when up-to-date computed style or layout information is needed for a script API that depends on it.

意思是說,瀏覽器通常還會在兩種情況下會產(chǎn)生樣式變更事件,一是滿足屏幕刷新頻率(不就是 requestAnimationFrame?),二是當(dāng) JS 腳本需要獲取最新的樣式布局信息時。

在 JS 代碼中,有些 API 被調(diào)用時,瀏覽器會同步地計算樣式和布局,頻繁調(diào)用這些 API(offset*/client*/getBoundingClientRect/scroll*/…等等)通常會成為性能瓶頸。

你了解 Transition 嗎?一起來深入了解下Transition!

然而在這個場景卻可以產(chǎn)生奇妙的化學(xué)反應(yīng):

if (!popup.classList.contains("active")) {   popup.style.display = "block";   popup.scrollWidth;   popup.classList.add("active"); }

注意看,我們只是 display 和 add class 之間讀取了一下 scrollWidth,甚至沒有賦值,過渡動畫就活過來了。

你了解 Transition 嗎?一起來深入了解下Transition!

原因是 scrollWidth 強(qiáng)制同步觸發(fā)了重排重繪,再下一行代碼時,popup 的 display 屬性已經(jīng)更新到 Render Tree 上了。

優(yōu)化方案四:過渡完了告訴我 onTransitionEnd

現(xiàn)在【出現(xiàn)】動畫已經(jīng)搞明白了,在看開源庫的源碼中發(fā)現(xiàn)像 vue, bootstrap, react-transition-group 等庫都是使用了 force reflow 的方法,而 antd 所使用的 css-animte 庫則是通過設(shè)置 setTimeout。

【消失】動畫還不夠優(yōu)雅,前面我們是直接寫死 setTimeout 600,讓元素在動畫結(jié)束時消失的。這樣編碼可復(fù)用性差,修改動畫時間還得改兩處地方(JS + CSS),有沒有更優(yōu)雅的實現(xiàn)?

popup.classList.remove("active");setTimeout(() => {     popup.style.display = "none"; }, 600);

文檔中也提到了 Transition Events,包括 transitionruntransitionstarttransitionendtransitioncancel,看名字就知道事件代表什么意思,這里可以用 transitionend 進(jìn)行代碼優(yōu)化。

if (!popup.classList.contains("active")) {     popup.style.display = "block";     popup.scrollWidth;     popup.classList.add("active"); } else {     popup.classList.remove("active");     popup.addEventListener('transitionend', () => {         popup.style.display = "none";     }, { once: true }) }

需要注意 transition events 同樣也有冒泡、捕獲的特性,如果有嵌套 transition 時需要留意 event.target

到這里我們已經(jīng)用原生 JS 完成了一個出現(xiàn)、消失的動畫實現(xiàn),完整的代碼在這里。文章的最后,我們參照 vue-transition 來開發(fā)一個 React Transition 的單個元素動畫過渡的最小實現(xiàn)。

仿 v-transition 實現(xiàn)一個 React Transition 組件

你了解 Transition 嗎?一起來深入了解下Transition!

根據(jù)動畫過程拆分成幾個過程:

  • enter 階段渲染 DOM 節(jié)點,初始化動畫初始狀態(tài)(添加 *-enter 類名)
  • enter-active 階段執(zhí)行 transition 過渡動畫(添加 *-enter-active 類名)
  • enter-active 過渡完成之后進(jìn)入正常展示階段(移除 *-enter-active 類名)

enter-to 和 leave-to 暫時用不上,leave 階段和 enter 基本一致也不再贅述。

直接看代碼:

export const CSSTransition = (props: Props) => {   const { children, name, active } = props;   const nodeRef = useRef<HTMLElement | null>(null);   const [renderDOM, setRenderDOM] = useState(active);    useEffect(() => {     requestAnimationFrame(() => {       if (active) {         setRenderDOM(true);         nodeRef.current?.classList.add(`${name}-enter`);         // eslint-disable-next-line @typescript-eslint/no-unused-expressions         nodeRef.current?.scrollWidth;         nodeRef.current?.classList.remove(`${name}-enter`);         nodeRef.current?.classList.add(`${name}-enter-active`);          nodeRef.current?.addEventListener("transitionend", (event) => {           if (event.target === nodeRef.current) {             nodeRef.current?.classList.remove(`${name}-enter-active`);           }         });       } else {         nodeRef.current?.classList.add(`${name}-leave`);         // eslint-disable-next-line @typescript-eslint/no-unused-expressions         nodeRef.current?.scrollWidth;         nodeRef.current?.classList.remove(`${name}-leave`);         nodeRef.current?.classList.add(`${name}-leave-active`);          nodeRef.current?.addEventListener("transitionend", (event) => {           if (event.target === nodeRef.current) {             nodeRef.current?.classList.remove(`${name}-leave-active`);             setRenderDOM(false);           }         });       }     });   }, [active, name]);    if (!renderDOM) {     return null;   }    return cloneElement(Children.only(children), {     ref: nodeRef   }); };

這個組件接收三個 props,分別是

  • children 需要做過渡動畫的 ReactElement,只允許傳一個 Element
  • name 過渡動畫的 css 類名前綴
  • active 布爾值,用于區(qū)分是進(jìn)場還是消失

使用方式:

<CSSTransition name="fade" active={active}>     // 一個需要做過渡動畫的 ReactElement </CssTransition>

借助 transition-delay,加一點技巧實現(xiàn) stagger 效果:

你了解 Transition 嗎?一起來深入了解下Transition!

完整的示例代碼在這里,注意:這只是個快速實現(xiàn)用于演示的示例,有非常多的問題沒有考慮在內(nèi),僅可用于學(xué)習(xí)參考。

結(jié)語

原本以為非常基礎(chǔ)簡單的知識點,分分鐘可以寫完這篇文章。沒想到中途查文檔,看資料,制作演示 DEMO 還是花了不少時間。好在整理資料的過程中也理清了很多知識點。希望這篇文章對你熟悉 Transition 動畫有所幫助 。

贊(0)
分享到: 更多 (0)
?
網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號
地师传人电影在线观看| 一边做一边潮喷30P| 国产啪精品视频网站免费尤物| 97夜夜澡人人双人人人喊| 亚洲国产另类久久久精品黑人| 色窝窝无码一区二区三区成人网站| 男女啪啪无遮挡免费网站| 久久精品熟女亚州AV麻豆| 国内精品人妻久久毛片APP| 国产成人无码久久久精品一| 啊轻点灬大JI巴太粗太长了在线| 又硬又粗又长又爽免费看| 亚洲老妈激情一区二区三区| 亚欧乱色国产精品免费九库| 天天狠天天透天天爱综合 | 亚洲AV无码成人影片在线观看 | 亚洲高清国产AV拍精品青青草原 | 国产成人亚洲综合无码18禁禁| 国产桃色无码视频在线观看 | 日韩精品久久久久久久电影蜜臀 | 白丝老师用腿夹得我好爽的视频| 69综合精品国产二区无码 | 亚州熟妇无码AV线播放| 亚洲性日韩精品一区二区三区| 亚洲精品第一国产综合亚AV| 有人有在线观看的片资源| YY8098影视理论无码专区| MM1313午夜视频在线观看| av资源在线观看少妇| JK白丝极品被CAO到流水呻吟| 丰满大屁股BWWBWWBWW| 公交车挺进朋友人妻的身体里| 粉嫩av一区二区夜夜嗨| 黑人顶到深处高潮颤抖| 精品久久久中文字幕人妻| 久久99国产精品久久99软件| 久久久久99精品成人片| 人妻丰满熟妇AV无码区不卡| 日本少妇XXⅩ熟睡侵犯| 日日噜噜夜夜爽爽| 思思久久99热只有频精品66| 天堂А√在线地址中文在线| 亚洲国产精品久久无码中文字蜜桃| 中文字幕三级人妻无码视频| FIREEXⅩ性欧美HD护士 | 亚洲国产精品久久久久爰| 99精产国电影品一二三产区区别| 国产高清在线精品一区二区三区| 国产精品久久这里只有精品 | 无人区码一码二码三码四码| 性色AV蜜臀AV色欲AV| 13小箩利洗澡无码视频网站| YELLOW片观看完整版| 国产一二三四区中| 久久大蕉香蕉免费| 奶头又大又白喷奶水AV| 忘忧草视频在线观看| 亚洲AV无码专区在线观看亚| 亚洲熟妇无码一区二区三区| 18一20亚洲GAY无套| 国产精品久久久久久超碰| 男男GV在线观看| 亚洲AVAV国产AV综合AV| 亚洲愉拍自拍欧美精品APP| 2018一本久道在线线观看| 国产精品任我爽爆在线播放| 精品少妇无码AV在线播放| 免费看片A级毛片免费看| 日本亚欧乱色视频在线| 亚洲日韩中文字幕无码一区| 777琪琪午夜理论电影网| 国产女主播白浆在线看| 欧美性受XXXX喷潮| 无码国产成人午夜在线观看| 亚洲日韩国产精品乱-久| 东京热无码AV一区二区| 国内精品国产三级国产AV| 人曾交互MOUSE农场| 无码人妻AV一二区二区三区| 亚洲国产婷婷香蕉久久久久久| 波多野结衣守望人妻理论| 国产午夜激无码AV毛片不| 久久躁狠狠躁夜夜AV| 人妻少妇伦在线无码| 亚洲色自偷自拍另类小说 | 色777狠狠狠综合| 亚洲AV无码专区国产乱码不卡| 波多野结衣的电影| 麻豆一二三区AV精品传媒| 日韩A片无码ⅩXXXX| 中文字幕三级人妻无码视频 | 亚洲浮力影院久久久久久| 99精产国品一二三产区区别电影 | 刺激战场未满十八岁能玩多久| 国精一二二产品无人区免费应用| 日韩麻豆乱婬一区二区三区| 亚洲H成年动漫在线观看网站| 自拍偷自拍亚洲精品10P| 国产AV导航大全精品| 久久久久99精品成人品| 日韩精品一区二区三区中文无码| 中文无码人妻有码人妻中文字幕| 国产成人综合在线视频| 人人鲁人人莫人人爱精品| 亚洲精品无码AV人在线观看| 被客人玩得站不起来大前端 | 军人边走边吮她的花蒂| 熟妇高潮精品一区二区三区| 亚洲欲色欲色XXXXX在线观看| 国产无遮挡无码很黄很污很刺激 | 欧美极品少妇做受| 亚洲AV成人无码一区二区三区在| 丰满熟妇大肉唇张开| 日本适合十八岁以上的护肤品男| 99在线精品免费视频九九视| 国产亚洲精品拍拍拍拍拍| 天美传媒MV免费观看软件特色 | 爆乳护士HD完整版在线播放| 女人张开腿让男人添| 亚洲成成熟女人专区| 国产精品一区二区国产馆蜜桃| 蜜桃国产乱码精品一区二区三区w| 我和岳M愉情XXXX| 国产69精品久久久久9999不| 日产无人区二线三线乱码| 不惑女人的扭曲生活| 人人妻人人澡人人爽人人精品图片 | 强 暴 疼 哭 处 女| 亚洲色偷偷AV男人的天堂| 荒野大镖客暴躁老太太| 色噜噜人妻丝袜aV先锋影音先| JEAⅠOUSVUE成熟HD| 人妻精品久久无码区| 爸爸缓慢有力送女儿的句子| 人妻天天爽夜夜爽一区二区| 菠萝菠萝蜜菠萝菠萝5| 日韩人妻潮喷中文在线视频| 第一次接黑人嫖客| 天堂SV在线最新版在线| 国产成人综合在线观看不卡| 午夜理论片YY6080影院| 成人亚洲AV日韩AV欧v| 男人边吃奶边做好爽免费视频 | 欧美人妻AⅤ中文字幕| 亚洲熟妇色XXXXXX爽| 久久久久久久99精品国产片| 一本之道AV不卡精品| 欧美JIZZ18性欧美| 影音先锋亚洲成AⅤ人在| 河南妇女毛浓浓BW| 我和闺蜜在KTV被八人伦| 国产乱码1卡二卡3卡四卡| 亚洲AV一二三又爽又色又色| 久久国产成人精品国产成人亚洲 | 久久夜色精品国产亚洲| 2018AV无码视频在线播放| 人妻免费一区二区三区最新| 俄罗斯人与ZOOM人的区别| 无码视频一区二区三区在线观看| 国产性生交XXXXX免费| 亚洲日产精品一二三四区| 蜜臀亚洲AV无码精品国产午夜| TPU色母和PA6色母的性能| 丝袜中文人妻无码有码久热| 国产偷国产偷亚洲清高网站| 亚洲色欲色欲WWW在线观看| 免费无码AV片在线观看中文 | YY8098影视理论无码专区| 熟女少妇一区二区三区| 激情 亚洲 成人小说 激情| 一区二区三区久久含羞草| 欧美一性一乱一交一视频| 国产成人精品亚洲午夜麻豆| 色8久久人人97超碰香蕉987 | 宝贝乖女小芳小雪| 无码国产精品一区二区免费VR| 精品久久久久久中文字幕大豆网| 中国老熟妇自拍HD发布| 两人做人爱视频在线观看| 伊人久久亚洲综合| 欧洲-级毛片内射| 国产精品门事件AV| 野花おっさんとわたし| 久久久久亚洲精品无码网址色欲 | 精品无码AV人在线观看| 97人人模人人爽人人少妇| 少妇无码一区二区二三区| 精品无码AV一区二区三区不卡| 99国产欧美久久久精品蜜桃| 无码国产69精品久久久久孕妇| 久久久久久久精品国产亚洲| 菠萝视频免费最新在线观看| 无码熟妇人妻在线视频| 老外和中国女人毛片免费视频| 大又大粗又爽又黄少妇毛片免费| 亚洲成AV人片在线播放无码| 欧洲乱码伦视频免费| 好男人2019在线视频播放观看| 99久久精品国产一区二区三区 | 一二三四视频社区在线播放中国 |