vue3-内置组件-Transition

基于状态变化的过渡和动画(常用)

建议多看几遍~~。然后动手去写写,学编程只有多动手才能有感觉。

内置组件:

它在任意别的组件中都可以被使用,无需注册。

Vue 提供了两个内置组件,可以帮助你制作基于状态变化的过渡和动画:

<Transition> 会在一个元素或组件进入和离开 DOM 时应用动画。本章节会介绍如何使用它。
<TransitionGroup> 会在一个 v-for 列表中的元素或组件被插入,移动,或移除时应用动画

Transition

进入或者离开的动画

  • 由 v-if 所触发的切换
  • 由 v-show 所触发的切换
  • 由特殊元素 <component> 切换的动态组件
  • 改变特殊的 key 属性
xml 复制代码
<script lang="ts" setup>
import { ref } from 'vue'

const show = ref(true)
</script>

<template>
  <div class="container">
    <button @click="show = !show">点击切换按钮状态</button>
    <Transition>
      <p v-if="show">hello</p>
    </Transition>
  </div>
</template>

<style lang="scss" scoped>
.container {
  /* 下面我们会解释这些 class 是做什么的 */
  .v-enter-active,
  .v-leave-active {
    transition: opacity 0.5s ease;
  }

  .v-enter-from,
  .v-leave-to {
    opacity: 0;
  }
}
</style>

<Transition> 仅支持单个元素或组件作为其插槽内容。如果内容是一个组件,这个组件必须仅有一个根元素。

当一个 <Transition> 组件中的元素被插入或移除时,会发生下面这些事情:

Vue 会自动检测目标元素是否应用了 CSS 过渡或动画。如果是,则一些 CSS 过渡 class 会在适当的时机被添加和移除。

如果有作为监听器的 JavaScript 钩子,这些钩子函数会在适当时机被调用。

如果没有探测到 CSS 过渡或动画、也没有提供 JavaScript 钩子,那么 DOM 的插入、删除操作将在浏览器的下一个动画帧后执行。

基于 CSS 的过渡效果

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-enter-active 和 v-leave-active 给我们提供了为进入和离开动画指定不同速度曲线的能力,我们将在下面的小节中看到一个示例。

为过渡效果命名

我们可以给 Transition 组件传一个 name prop 来声明一个过渡效果名:

ini 复制代码
<Transition name="fade">
  ...
</Transition>

对于一个有名字的过渡效果,对它起作用的过渡 class 会以其名字而不是 v 作为前缀。比如,上方例子中被应用的 class 将会是 fade-enter-active 而不是 v-enter-active。这个"fade"过渡的 class 应该是这样:

css 复制代码
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
CSS 的 transition
  1. Transition 一般都会搭配原生 CSS 过渡一起使用,正如你在上面的例子中所看到的那样。

  2. 这个 transition CSS 属性是一个简写形式,使我们可以一次定义一个过渡的各个方面,包括需要执行动画的属性、持续时间和速度曲线。

使用不同的持续时间和速度曲线来过渡多个属性示例:

xml 复制代码
<script lang="ts" setup>
import { ref } from 'vue'
const show = ref(true)
</script>

<template>
  <div class="container">
    <button @click="show = !show">点击切换按钮状态</button>
    <Transition name="slide-fade">
      <p v-if="show">持续时间和速度曲线控制</p>
    </Transition>
  </div>
</template>

<style lang="scss" scoped>
.container {
  /*
  进入和离开动画可以使用不同
  持续时间和速度曲线。
*/
  .slide-fade-enter-active {
    transition: all 0.3s ease-out;
  }

  .slide-fade-leave-active {
    transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
  }

  .slide-fade-enter-from,
  .slide-fade-leave-to {
    transform: translateX(20px);
    opacity: 0;
  }
}
</style>
CSS 的 animation

原生 CSS 动画和 CSS transition 的应用方式基本上是相同的,只有一点不同,那就是 *-enter-from 不是在元素插入后立即移除,而是在一个 animationend 事件触发时被移除。

对于大多数的 CSS 动画,我们可以简单地在 _-enter-active 和 _-leave-active class 下声明它们。下面是一个示例:

xml 复制代码
<script lang="ts" setup>
import { ref } from 'vue'
const show = ref(true)
</script>

<template>
  <div class="container">
    <button @click="show = !show">点击切换状态</button>
    <Transition name="bounce">
      <p v-if="show" style="text-align: center">这是 CSS transition 示例</p>
    </Transition>
  </div>
</template>

<style lang="scss" scoped>
.container {
  text-align: center;
  .bounce-enter-active {
    animation: bounce-in 0.5s;
  }
  .bounce-leave-active {
    animation: bounce-in 0.5s reverse;
  }
  @keyframes bounce-in {
    0% {
      transform: scale(0);
    }
    50% {
      transform: scale(1.25);
    }
    100% {
      transform: scale(1);
    }
  }
}
</style>
自定义过渡 class

你也可以向 <Transition> 传递以下的 props 来指定自定义的过渡 class:

  • enter-from-class
  • enter-active-class
  • enter-to-class
  • leave-from-class
  • leave-active-class
  • leave-to-class 你传入的这些 class 会覆盖相应阶段的默认 class 名。这个功能在你想要在 Vue 的动画机制下集成其他的第三方 CSS 动画库时非常有用,比如 Animate.css:

安装 animate.css 动画库,(已安装的可以忽略)

css 复制代码
pnpm install animate.css --save
xml 复制代码
<!-- 假设你已经在页面中引入了 Animate.css -->
<script lang="ts" setup>
import { ref } from 'vue'
import 'animate.css'
const show = ref(true)
</script>

<template>
  <div class="container">
    <button @click="show = !show">点击切换状态</button>
    <Transition
      name="custom-classes"
      enter-active-class="animate__animated animate__zoomIn "
      leave-active-class="animate__animated animate__zoomOut"
    >
      <p v-if="show">引入animate动画库</p>
    </Transition>
  </div>
</template>

<style lang="scss" scoped>
.container {
  text-align: center;
}
</style>
同时使用 transition 和 animation

Vue 需要附加事件监听器,以便知道过渡何时结束。可以是 transitionend 或 animationend,这取决于你所应用的 CSS 规则。如果你仅仅使用二者的其中之一,Vue 可以自动探测到正确的类型。

然而在某些场景中,你或许想要在同一个元素上同时使用它们两个。举例来说,Vue 触发了一个 CSS 动画,同时鼠标悬停触发另一个 CSS 过渡。此时你需要显式地传入 type prop 来声明,告诉 Vue 需要关心哪种类型,传入的值是 animation 或 transition:

伪代码:

css 复制代码
<Transition type="animation">...</Transition>
深层级过渡与显式过渡时长

尽管过渡 class 仅能应用在 <Transition> 的直接子元素上,我们还是可以使用深层级的 CSS 选择器,在深层级的元素上触发过渡效果。

ini 复制代码
<Transition name="nested">
  <div v-if="show" class="outer">
    <div class="inner">
      Hello
    </div>
  </div>
</Transition>
css 复制代码
/* 应用于嵌套元素的规则 */
.nested-enter-active .inner,
.nested-leave-active .inner {
  transition: all 0.3s ease-in-out;
}

.nested-enter-from .inner,
.nested-leave-to .inner {
  transform: translateX(30px);
  opacity: 0;
}

/* ... 省略了其他必要的 CSS */

我们甚至可以在深层元素上添加一个过渡延迟,从而创建一个带渐进延迟的动画序列:

css 复制代码
/* 延迟嵌套元素的进入以获得交错效果 */
.nested-enter-active .inner {
  transition-delay: 0.25s;
}

然而,这会带来一个小问题。默认情况下,<Transition> 组件会通过监听过渡根元素上的第一个 transitionend 或者 animationend 事件来尝试自动判断过渡何时结束。而在嵌套的过渡中,期望的行为应该是等待所有内部元素的过渡完成。

在这种情况下,你可以通过向 <Transition> 组件传入 duration prop 来显式指定过渡的持续时间 (以毫秒为单位)。总持续时间应该匹配延迟加上内部元素的过渡持续时间:

ini 复制代码
<Transition :duration="550">...</Transition>

如果有必要的话,你也可以用对象的形式传入,分开指定进入和离开所需的时间:

ini 复制代码
<Transition :duration="550">...</Transition>
性能考量

你可能注意到我们上面例子中展示的动画所用到的 CSS 属性大多是 transform 和 opacity 之类的。用这些属性制作动画非常高效,因为:

他们在动画过程中不会影响到 DOM 结构,因此不会每一帧都触发昂贵的 CSS 布局重新计算。

大多数的现代浏览器都可以在执行 transform 动画时利用 GPU 进行硬件加速。

相比之下,像 height 或者 margin 这样的属性会触发 CSS 布局变动 ,因此执行它们的动画效果更昂贵,需要谨慎使用。我们可以在 CSS-Triggers(csstriggers.com/) 这类的网站查询哪些属性会在执行动画时触发 CSS 布局变动。

JavaScript 钩子

你可以通过监听 <Transition> 组件事件的方式在过渡过程中挂上钩子函数:

ini 复制代码
<Transition
  @before-enter="onBeforeEnter"
  @enter="onEnter"
  @after-enter="onAfterEnter"
  @enter-cancelled="onEnterCancelled"
  @before-leave="onBeforeLeave"
  @leave="onLeave"
  @after-leave="onAfterLeave"
  @leave-cancelled="onLeaveCancelled"
>
  <!-- ... -->
</Transition>
scss 复制代码
// 在元素被插入到 DOM 之前被调用
// 用这个来设置元素的 "enter-from" 状态
function onBeforeEnter(el) {}

// 在元素被插入到 DOM 之后的下一帧被调用
// 用这个来开始进入动画
function onEnter(el, done) {
  // 调用回调函数 done 表示过渡结束
  // 如果与 CSS 结合使用,则这个回调是可选参数
  done()
}

// 当进入过渡完成时调用。
function onAfterEnter(el) {}

// 当进入过渡在完成之前被取消时调用
function onEnterCancelled(el) {}

// 在 leave 钩子之前调用
// 大多数时候,你应该只会用到 leave 钩子
function onBeforeLeave(el) {}

// 在离开过渡开始时调用
// 用这个来开始离开动画
function onLeave(el, done) {
  // 调用回调函数 done 表示过渡结束
  // 如果与 CSS 结合使用,则这个回调是可选参数
  done()
}

// 在离开过渡完成、
// 且元素已从 DOM 中移除时调用
function onAfterLeave(el) {}

// 仅在 v-show 过渡中可用
function onLeaveCancelled(el) {}

这些钩子可以与 CSS 过渡或动画结合使用,也可以单独使用。

在使用仅由 JavaScript 执行的动画时,最好是添加一个 :css="false" prop。这显式地向 Vue 表明可以跳过对 CSS 过渡的自动探测。除了性能稍好一些之外,还可以防止 CSS 规则意外地干扰过渡效果。

ini 复制代码
<Transition
  ...
  :css="false"
>
  ...
</Transition>

在有了 :css="false" 后,我们就自己全权负责控制什么时候过渡结束了。这种情况下对于 @enter 和 @leave 钩子来说,回调函数 done 就是必须的。否则,钩子将被同步调用,过渡将立即完成。

这里是使用 GreenSock 库(gsap.com/)执行动画的一个示例,...%25E6%2589%25A7%25E8%25A1%258C%25E5%258A%25A8%25E7%2594%25BB%25E7%259A%2584%25E4%25B8%2580%25E4%25B8%25AA%25E7%25A4%25BA%25E4%25BE%258B%25EF%25BC%258C%25E4%25BD%25A0%25E4%25B9%259F%25E5%258F%25AF%25E4%25BB%25A5%25E4%25BD%25BF%25E7%2594%25A8%25E4%25BB%25BB%25E4%25BD%2595%25E4%25BD%25A0%25E6%2583%25B3%25E8%25A6%2581%25E7%259A%2584%25E5%25BA%2593%25EF%25BC%258C%25E6%25AF%2594%25E5%25A6%2582 "https://gsap.com/)%E6%89%A7%E8%A1%8C%E5%8A%A8%E7%94%BB%E7%9A%84%E4%B8%80%E4%B8%AA%E7%A4%BA%E4%BE%8B%EF%BC%8C%E4%BD%A0%E4%B9%9F%E5%8F%AF%E4%BB%A5%E4%BD%BF%E7%94%A8%E4%BB%BB%E4%BD%95%E4%BD%A0%E6%83%B3%E8%A6%81%E7%9A%84%E5%BA%93%EF%BC%8C%E6%AF%94%E5%A6%82") Anime.js (animejs.com/) 或者 Motion One(motion.dev/)。%25E3%2580%2582 "https://motion.dev/)%E3%80%82")

可复用过渡效果

得益于 Vue 的组件系统,过渡效果是可以被封装复用的。要创建一个可被复用的过渡,我们需要为 <Transition> 组件创建一个包装组件,并向内传入插槽内容:

xml 复制代码
<!-- MyTransition.vue -->
<script>
// JavaScript 钩子逻辑...
</script>

<template>
  <!-- 包装内置的 Transition 组件 -->
  <Transition
    name="my-transition"
    @enter="onEnter"
    @leave="onLeave">
    <slot></slot> <!-- 向内传递插槽内容 -->
  </Transition>
</template>

<style>
/*
  必要的 CSS...
  注意:避免在这里使用 <style scoped>
  因为那不会应用到插槽内容上
*/
</style>

现在 MyTransition 可以在导入后像内置组件那样使用了:

xml 复制代码
<MyTransition>
  <div v-if="show">Hello</div>
</MyTransition>

出现时过渡

如果你想在某个节点初次渲染时应用一个过渡效果,你可以添加 appear prop:

css 复制代码
<Transition appear>
  ...
</Transition>

元素间过渡

除了通过 v-if / v-show 切换一个元素,我们也可以通过 v-if / v-else / v-else-if 在几个组件间进行切换,只要确保任一时刻只会有一个元素被渲染即可:

xml 复制代码
<Transition>
  <button v-if="docState === 'saved'">Edit</button>
  <button v-else-if="docState === 'edited'">Save</button>
  <button v-else-if="docState === 'editing'">Cancel</button>
</Transition>
``

过渡模式

在之前的例子中,进入和离开的元素都是在同时开始动画的,因此我们不得不将它们设为 position: absolute 以避免二者同时存在时出现的布局问题。

然而,很多情况下这可能并不符合需求。我们可能想要先执行离开动画,然后在其完成之后再执行元素的进入动画。手动编排这样的动画是非常复杂的,好在我们可以通过向 <Transition> 传入一个 mode prop 来实现这个行为:

ini 复制代码
<Transition mode="out-in">
  ...
</Transition>

将之前的例子改为 mode="out-in" 后是这样:

<Transition> 也支持 mode="in-out",虽然这并不常用。

组件间过渡

<Transition> 也可以作用于动态组件之间的切换:

ruby 复制代码
<Transition name="fade" mode="out-in">
  <component :is="activeComponent"></component>
</Transition>

动态过渡

<Transition> 的 props (比如 name) 也可以是动态的!这让我们可以根据状态变化动态地应用不同类型的过渡:

xml 复制代码
<Transition :name="transitionName">
  <!-- ... -->
</Transition>

这个特性的用处是可以提前定义好多组 CSS 过渡或动画的 class,然后在它们之间动态切换。

你也可以根据你的组件的当前状态在 JavaScript 过渡钩子中应用不同的行为。最后,创建动态过渡的终极方式还是创建可复用的过渡组件,并让这些组件根据动态的 props 来改变过渡的效果。掌握了这些技巧后,就真的只有你想不到,没有做不到的了。

相关推荐
Jiaberrr3 小时前
前端实战:使用JS和Canvas实现运算图形验证码(uniapp、微信小程序同样可用)
前端·javascript·vue.js·微信小程序·uni-app
everyStudy3 小时前
JS中判断字符串中是否包含指定字符
开发语言·前端·javascript
城南云小白3 小时前
web基础+http协议+httpd详细配置
前端·网络协议·http
前端小趴菜、3 小时前
Web Worker 简单使用
前端
web_learning_3213 小时前
信息收集常用指令
前端·搜索引擎
tabzzz4 小时前
Webpack 概念速通:从入门到掌握构建工具的精髓
前端·webpack
LvManBa4 小时前
Vue学习记录之六(组件实战及BEM框架了解)
vue.js·学习·rust
200不是二百4 小时前
Vuex详解
前端·javascript·vue.js
滔滔不绝tao4 小时前
自动化测试常用函数
前端·css·html5
LvManBa4 小时前
Vue学习记录之三(ref全家桶)
javascript·vue.js·学习