徹底搞懂Transition內置組件

目錄 前言 Transition 內置組件 觸發條件 再分類 六個過渡時機 Transition 組件 CSS transition 屬性 核心原理 實現 Transition 組件 原生 DOM 如何實現過渡? 原生 DOM 元素移動示例 進場動效 離場動
目錄
  • 前言
  • Transition 內置組件
    • 觸發條件
    • 再分類
    • 六個過渡時機
    • Transition 組件 & CSS transition 屬性
    • 核心原理
  • 實現 Transition 組件
    • 原生 DOM 如何實現過渡?
    • 原生 DOM 元素移動示例
    • 進場動效
    • 離場動效
    • 實現 Transition 組件
  • 最后

    前言

    <Transition>?作為一個?Vue?中的內置組件,它可以將?進入動畫?和?離開動畫?應用到通過?默認插槽?傳遞給目標元素或組件上。

    也許你有在使用,但是一直不清楚它的原理或具體實現,甚至不清楚其內部提供的各個?class?到底怎么配合使用,想看源碼又被其中各種引入搞得七葷八素...

    本篇文章就以?Transition?組件為核心,探討其核心原理的實現,文中不會對其各個屬性再做額外解釋,畢竟這些看文檔就夠了,希望能夠給你帶來幫助!!!

    Transition 內置組件

    觸發條件

    <Transition>?組件的?進入動畫?或?離開動畫?可通過以下的條件之一觸發:

    • 由?v-if?所觸發的切換
    • 由?v-show?所觸發的切換
    • 由特殊元素?<component name="x">?切換的動態組件
    • 改變特殊的?key?屬性

    再分類

    其實我們可以將以上情況進行?再分類:

    • 組件?掛載?和?銷毀

      • v-if?的變化
      • <component name="x">?的變化
      • key?的變化
    • 組件?樣式?屬性?display: none | x?設置

      • v-show?的變化

    ?【擴展】v-if?和?v-for?一起使用時,在?Vue2?和?Vue3?中的不同

    • 在?Vue2?中,當它們處于同一節點時,v-for?的優先級比?v-if?更高,即?v-if?將分別重復運行于每個?v-for?循環中,也就是?v-if?可以正常訪問?v-for?中的數據
    • 在?Vue3?中,當它們處于同一節點時,v-if?的優先級比?v-for?更高,即此時只要?v-if?的值為?false?則?v-for?的列表就不會被渲染,也就是?v-if?不能訪問到?v-for?中的數據

    六個過渡時機

    總結起來就分為?進入?和?離開?動畫的?初始狀態、生效狀態、結束狀態,具體如下:

    • v-enter-from

      • 進入?動畫的?起始狀態
      • 在元素插入之前添加,在元素插入完成后的?下一幀移除
    • v-enter-active

      • 進入?動畫的?生效狀態,應用于整個進入動畫階段
      • 在元素被插入之前添加,在過渡或動畫完成之后移除
      • 這個?class?可以被用來定義進入動畫的持續時間、延遲與速度曲線類型
    • v-enter-to

      • 進入?動畫的?結束狀態
      • 在元素插入完成后的下一幀被添加 (也就是?v-enter-from?被移除的同時),在過渡或動畫完成之后移除
    • v-leave-from

      • 離開?動畫的?起始狀態
      • 在離開過渡效果被觸發時立即添加,在一幀后被移除
    • v-leave-active

      • 離開?動畫的?生效狀態,應用于整個離開動畫階段
      • 在離開過渡效果被觸發時立即添加,在?過渡或動畫完成之后移除
      • 這個?class?可以被用來定義離開動畫的持續時間、延遲與速度曲線類型
    • v-leave-to

      • 離開?動畫的?結束狀態
      • 在一個離開動畫被觸發后的?下一幀?被添加 (即?v-leave-from?被移除的同時),在?過渡或動畫完成之后移除

    其中的?v?前綴是允許修改的,可以?<Transition>?組件傳一個?name?的?prop?來聲明一個過渡效果名,如下就是將?v?前綴修改為 **`
    modal `** 前綴:

    <Transition name="modal"> ... </Transition>

    Transition 組件 & CSS transition 屬性

    以上這個簡單的效果,核心就是兩個時機:

    • v-enter-active?進入動畫的?生效狀態
    • v-leave-active?離開動畫的?生效狀態

    再配合簡單的?CSS?過渡屬性就可以達到效果,代碼如下:

    <template>
      <div class="home">
        <transition name="golden">
          <!-- 金子列表 -->
          <div class="golden-box" v-show="show">
            <img
              class="golden"
              :key="idx"
              v-for="idx in 3"
              src="http://news.558idc.com/assets/golden.jpg"
            />
          </div>
        </transition>
      </div>
      <!-- 錢袋子 -->
      <img class="purse" @click="show = !show" src="http://news.558idc.com/assets/purse.png" alt="" />
    </template>
    <script setup lang="ts">
    import { ref, computed } from 'vue'
    const show = ref(true)
    </script>
    <style lang="less" scoped>
    .home {
      min-height: 66px;
    }
    .golden-box {
      transition: all 1s ease-in;
      .golden {
        width: 100px;
        position: fixed;
        transform: translate3d(0, 0, 0);
        transition: all .4s;
        &:nth-of-type(1) {
          left: 45%;
          top: 100px;
        }
        &:nth-of-type(2) {
          left: 54%;
          top: 50px;
        }
        &:nth-of-type(3) {
          right: 30%;
          top: 100px;
        }
      }
      &.golden-enter-active {
        .golden {
          transform: translate3d(0, 0, 0);
          transition-timing-function: cubic-bezier(0, 0.57, 0.44, 1.97);
        }
        .golden:nth-of-type(1) {
          transition-delay: 0.1s;
        }
        .golden:nth-of-type(2) {
          transition-delay: 0.2s;
        }
        .golden:nth-of-type(3) {
          transition-delay: 0.3s;
        }
      }
      &.golden-leave-active {
        .golden:nth-of-type(1) {
          transform: translate3d(150px, 140px, 0);
          transition-delay: 0.3s;
        }
        .golden:nth-of-type(2) {
          transform: translate3d(0, 140px, 0);
          transition-delay: 0.2s;
        }
        .golden:nth-of-type(3) {
          transform: translate3d(-100px, 140px, 0);
          transition-delay: 0.1s;
        }
      }
    }
    .purse {
      position: fixed;
      width: 200px;
      margin-top: 100px;
      cursor: pointer;
    }
    </style>

    當然動畫的效果是多種多樣的,不僅只是局限于這一種,例如可以配合:

    • CSS?的?transition?過渡屬性(上述例子使用的方案)
    • CSS?的?animation?動畫屬性

    gsap 庫

    核心原理

    通過上述內容其實不難發現其核心原理就是:

    • 當?組件(DOM)?被?掛載?時,將過渡動效添加到該?DOM?元素上
    • 當?組件(DOM)?被?卸載?時,不是直接卸載,而是等待附加到?DOM?元素上的?動效執行完成,然后在真正執行卸載操作,即?延遲卸載時機

    在上述的過程中,<Transition>?組件會為?目標組件/元素?通過添加不同的?class?來定義?初始、生效、結束?三個狀態,當進入下一個狀態時會把上一個狀態對應的?class?移除。

    那么你可能會問了,v-show?的形式也不符合?掛載/卸載?的形式呀,畢竟它只是在修改?DOM?元素的?display: none | x?的樣式!

    讓源碼中的注釋來回答:

    v-if<component name="x">key?控制組件?顯示/隱藏?的方式是?掛載/卸載?組件,而?v-show?控制組件?顯示/隱藏?的方式是?修改/重置?display: none | x?屬性值,從本質上看方式不同,但從結果上看都屬于控制組件的?顯示/隱藏,即功能是一致的,而這里所說的?掛載/卸載?是針對大部分情況來說的,畢竟四種觸發方式中就有三種符合此情況。

    實現 Transition 組件

    所謂?Transition?組件畢竟是 Vue 的內置組件,換句話說,組件的編寫要符合 Vue 的規范(即?聲明式寫法),但為了更好的理解核心原理,我們應該從?原生 DOM?的過渡開始(即?命令式寫法)探討。

    原生 DOM 如何實現過渡?

    所謂的?過渡動效?本質上就是一個 DOM 元素在?兩種狀態間的轉換,瀏覽器?會根據我們設置的過渡效果?自行完成 DOM 元素的過渡。

    而?狀態的轉換?指的就是?初始化狀態?和?結束狀態?的轉換,并且配合 CSS 中的?transition?屬性就可以實現兩個狀態間的過渡,即?運動過程。

    原生 DOM 元素移動示例

    假設要為一個元素在垂直方向上添加進場動效:從?原始位置?向上移動?200px?的位置,然后在?1s?內運動回?原始位置。

    進場動效

    用 CSS 描述

    // 描述物體
      .box {
        width: 100px;
        height: 100px;
        background-color: red;
        box-shadow: 0 0 8px;
        border-radius: 50%;
      }
      // 初始狀態
      .enter-from {
        transform: translateY(-200px);
      }
      // 運動過程
      .enter-active {
        transition: transform 1s ease-in-out;
      }
      // 結束狀態
      .enter-to {
        transform: translateY(0);
      }

    ?用 JavaScript 描述

    // 創建元素
    const div = document.createElement('div')
    div.classList.add('box')
    // 添加 初始狀態 和 運動過程
    div.classList.add('enter-from')
    div.classList.add('enter-active')
    // 將元素添加到頁面上
    document.body.appendChild(div)
    // 切換元素狀態
    div.classList.remove('enter-from')
    div.classList.add('enter-to')

    從?命令式編程?的步驟上來看,似乎每一步都沒有問題,但實際的過渡動畫是不會生效的,雖然在代碼中我們有?狀態的切換,但這個切換的操作對于?瀏覽器?來講是在?同一幀中進行的,所以只會渲染?最終狀態,即?enter-to?類所指向的狀態。

    requestAnimationFrame 實現下一幀的變化

    window.requestAnimationFrame(callback)?會在瀏覽器在?下次重繪之前?調用指定的?回調函數?用于更新動畫。

    也就是說,單個的?requestAnimationFrame()?方法是在?當前幀?中執行的,也就是如果想要在?下一幀?中執行就需要使用兩個?requestAnimationFrame()?方法嵌套的方式來實現,如下:

    // 嵌套的 requestAnimationFrame 實現在下一幀中,切換元素狀態
      requestAnimationFrame(() => {
        requestAnimationFrame(() => {
          div.classList.remove("enter-from");
          div.classList.add("enter-to");
        });
      });

    ?transitionend 事件監聽動效結束

    以上就完成元素的?進入動效,那么在動效結束之后,別忘了將原本和?進入動效?相關的?類?移除掉,可以通過?transitionend 事件?監聽動效是否結束,如下

    // 嵌套的 requestAnimationFrame 實現在下一幀中,切換元素狀態
      requestAnimationFrame(() => {
        requestAnimationFrame(() => {
          div.classList.remove("enter-from");
          div.classList.add("enter-to");
          // 動效結束后,移除和動效相關的類
          div.addEventListener("transitionend", () => {
            div.classList.remove("enter-to");
            div.classList.remove("enter-active");
          });
        });
      });

    以上就是?進場動效?的實現,如下:

    離場動效

    有了進場動效的實現過程,在定義?離場動效?時就可以選擇和?進場動效?相對應的形式,即?初始狀態、過渡過程、結束狀態。

    用 CSS 描述

    // 初始狀態
      .leave-from {
        transform: translateY(0);
      }
      // 過渡狀態
      .leave-active {
        transition: transform 2s ease-out;
      }
      // 結束狀態
      .leave-to {
        transform: translateY(-300px);
      }

    ?用 JavaScript 描述

    所謂的?離場?就是指?DOM 元素?的?卸載,但因為要有離場動效要展示,所以不能直接卸載對應的元素,而是要?等待離場動效結束之后在進行卸載。

    為了直觀一些,我們可以添加一個離場的按鈕,用于觸發離場動效。

    // 創建離場按鈕
      const btn = document.createElement("button");
      btn.innerText = "離場";
      document.body.appendChild(btn);
      // 綁定事件
      btn.addEventListener("click", () => {
        // 設置離場 初始狀態 和 運動過程
        div.classList.add("leave-from");
        div.classList.add("leave-active");
        // 嵌套的 requestAnimationFrame 實現在下一幀中,切換元素狀態
        requestAnimationFrame(() => {
          requestAnimationFrame(() => {
            div.classList.remove("leave-from");
            div.classList.add("leave-to");
            // 動效結束后,移除和動效相關的類
            div.addEventListener("transitionend", () => {
              div.classList.remove("leave-to");
              div.classList.remove("leave-active");
              // 離場動效結束,移除目標元素
              div.remove();
            });
          });
        });
      });

    離場動效,如下:

    實現 Transition 組件

    以上的實現過程,可以將其進行抽象化為三個階段:

    • beforeEnter
    • enter
    • leave

    現在要從?命令式編程?轉向?聲明式編程?了,因為我們要去編寫?Vue 組件?了,即基于?VNode?節點來實現,為了和普通的?VNode?作為區分,Vue?中會為目標元素的?VNode?節點上添加?transition?屬性:

    • Transition 組件?本身不會渲染任何額外的內容,它只是通過?默認插槽?讀取過渡元素,并渲染需要過渡的元素
    • Transition 組件?作用,是在過渡元素的?VNode?節點上添加和?transition?相關的?鉤子函數
    <script lang="ts">
    import { defineComponent } from 'vue';
    const nextFrame = (callback: () => unknown) => {
      requestAnimationFrame(() => {
        requestAnimationFrame(callback)
      })
    }
    export default defineComponent({
      name: 'Transition',
      setup(props, { slots }) {
        // 返回 render 函數
        return () => {
          // 通過默認插槽,獲取目標元素
          const innerVNode = (slots as any).default()
          // 為目標元素添加 transition 相關鉤子
          innerVNode.transition = {
            beforeEnter(el: any) {
              console.log(111)
              // 設置 初始狀態 和 運動過程
              el.classList.add("enter-from");
              el.classList.add("enter-active");
            },
            enter(el: any) {
              // 在下一幀切換狀態
              nextFrame(() => {
                // 切換狀態
                el.classList.remove("enter-from");
                el.classList.add("enter-to");
                // 動效結束后,移除和動效相關的類
                el.addEventListener("transitionend", () => {
                  el.classList.remove("enter-to");
                  el.classList.remove("enter-active");
                });
              })
            },
            leave(el: any) {
              // 設置離場 初始狀態 和 運動過程
              el.classList.add("leave-from");
              el.classList.add("leave-active");
              // 在下一幀中,切換元素狀態
              nextFrame(() => {
                // 切換元素狀態
                el.classList.remove("leave-from");
                el.classList.add("leave-to");
                // 動效結束后,移除和動效相關的類
                el.addEventListener("transitionend", () => {
                  el.classList.remove("leave-to");
                  el.classList.remove("leave-active");
                  // 離場動效結束,移除目標元素
                  el.remove();
                });
              })
            }
          }
          // 返回修改過的 VNode
          return innerVNode
        }
      }
    })
    </script>

    最后

    從整體來看,Transition 組件?的核心并不算復雜,特別是以?命令式編程?實現之后,但話說回來在?Vue?源碼中實現的還是很全面的,比如:

    • 提供?props?實現用戶自定義類名
    • 提供?內置模式,即先進后出(in-out)、后進先出(enter-to
    • 支持?v-show?方式觸發過渡效果

    以上就是徹底搞懂Transition內置組件的詳細內容,更多關于Transition內置組件的資料請關注技圈網其它相關文章!

    聲明:所有內容來自互聯網搜索結果,不保證100%準確性,僅供參考。如若本站內容侵犯了原著者的合法權益,可聯系我們進行處理。
    發表評論
    更多 網友評論0 條評論)
    暫無評論

    返回頂部

    主站蜘蛛池模板: 花传媒季app| 久久精品中文字幕一区| 99精品国产在热久久无码| 精品一区二区三区电影| 幻女free性zozo交| 国产99久久亚洲综合精品| 久久久久久a亚洲欧洲AV| 艾粟粟小青年宾馆3p上下| 日本一区二区三| 国产乱女乱子视频在线播放| 久久99精品久久只有精品| 老师好大好爽办公室视频| 成全高清视频免费观看| 冻千秋的堕落h污文冬妃| а天堂中文最新一区二区三区| 男生和女生污污的视频| 城中村找个白皙丰满妇女在线播放 | 亚洲一区二区三区影院| 中文字幕激情视频| 日韩高清国产一区在线| 国产大片91精品免费看3| 久久久久久青草大香综合精品| 美女毛片一区二区三区四区| 小明天天看成人免费看| 亚洲香蕉在线观看| 18岁大陆女rapper欢迎你| 最新视频-88av| 国产一级性生活片| 一本色道久久88亚洲精品综合| 激情综合色五月丁香六月亚洲 | 三年片在线观看免费观看大全中国 | 绿巨人app入口| 天堂资源bt种子在线| 亚洲成av人片在线观看无码 | 澡人人澡人澡人人澡天天| 国产精品线在线精品国语| 九九视频在线观看视频6| 花季传媒app下载免费观看大全| 婷婷国产偷v国产偷v亚洲| 亚洲日韩久久综合中文字幕| 91成人免费版|