vue3.x之 过渡&动画

一、动画基本使用

1. vue的transition动画

Vue 提供了 transition 的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡:

  • 条件渲染 (使用 v-if)条件展示 (使用 v-show)
  • 动态组件
  • 组件根节点
 示例
<template>
  <!-- 触发器 -->
  <button @click="btnClick">显式/隐藏</button>

  <!-- 执行动画部分,通过 transition 包裹 -->
  <transition name="fade">
    <h2 v-if="isShow">Hello Vue3</h2>
  </transition>
</template>

<script setup>
import { ref } from "vue";

const isShow = ref(true);
const btnClick = () => {
  isShow.value = !isShow.value;
};
</script>

<style scoped>
/* 过渡起始状态 */
.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

/*过渡结束状态*/
.fade-enter-to,
.fade-leave-from {
  opacity: 1;
}

/* 过渡阶段 */
.fade-enter-active,
.fade-leave-active {
  transition: opacity 2s ease;
}
</style>

2. transition组件的原理

🐡 当插入或删除包含在 transition 组件中的元素时,Vue 将会做以下处理:
  • 自动嗅探目标元素是否应用了CSS过渡或者动画,如果有,那么在恰当的时机添加/删除 CSS类名
  • 如果 transition 组件提供了JavaScript钩子函数,这些钩子函数将在恰当的时机被调用
  • 如果没有找到JavaScript钩子并且也没有检测到CSS过渡/动画,DOM插入、删除操作将会立即执行
🐡 事实上Vue就是帮助我们在class之间来回切换完成的动画:
  • v-enter-from:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除
  • v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数
  • v-enter-to:定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter-from 被移除),在过渡/动画完成之后移除
  • v-leave-from:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除
  • v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数
  • v-leave-to:定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave-from 被删除),在过渡/动画完成之后移除 
🐡 class 添加时机和命名规则
  • 添加时机

  • class的name命名规则如下:

    🦋 如果使用的是一个没有name的transition,那么所有的class是以 v- 作为默认前缀

    /* 进入时 */
    .v-enter-from{
      opacity: 0;
    }
    .v-enter-active{
      transition: .3s;
    }
    .v-enter-to{
      opacity: 1;
    }
    /* 离开时 */
    .v-leave-from{
      opacity: 1;
    }
    .v-leave-active{
      transition: 1s;
    }
    .v-leave-to{
      opacity: 0;
    }

    🦋 如果添加了一个name属性,比如 <transtion name="fade">,那么所有的class会以 fade- 开头

    /* 进入时 */
    .fade-enter-from{
      opacity: 0;
    }
    .fade-enter-active{
      transition: .3s;
    }
    .fade-enter-to{
      opacity: 1;
    }
    /* 离开时 */
    .fade-leave-from{
      opacity: 1;
    }
    .fade-leave-active{
      transition: 1s;
    }
    .fade-leave-to{
      opacity: 0;
    }

3. css 动画

通过 animation 实现动画

<!-- 模版 -->
<template>
  <div>
    <button @click="isShow = !isShow">切换</button>
  </div>

  <transition name="fade">
    <h2 class="title" v-if="isShow">
      它们是一片朦胧的温馨与寂寥,是一片成熟的希望与绝望,它们的领地只有两处:心与坟墓。比如说邮票,有些是用于寄信的,有些仅仅是为了收藏。
    </h2>
  </transition>
</template>

<script setup>
import { ref } from "vue";
const isShow = ref(true);
</script>

<style scoped>
.title {
  display: inline-block;
}
.fade-enter-active {
  animation: anim 1s ease;
}
.fade-leave-active {
  animation: leaveAnim 1s ease;
}

/* 帧动画 */
/* 开始 */
@keyframes anim {
  0% {
    transform: scale(0);
    opacity: 0;
  }
  50% {
    transform: scale(1.2);
    opacity: 0.5;
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}
/* 离开 */
@keyframes leaveAnim {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-500px);
  }
}
</style>

4. 过渡和动画同时设置

Vue为了知道过渡的完成,内部是在监听 transitionend 或 animationend,到底使用哪一个取决于元素应用的CSS规则:如果我们只是使用了其中的一个,那么Vue能自动识别类型并设置监听

如果同时使用过渡和动画(开发中应该避免同时使用),并且某一个动画执行结束时,另外一个动画还没有结束

🐡 在这种情况下,我们可以设置 type 属性为 animation 或者 transition 来明确的告知Vue监听的类型
🐡 可以显示的来指定过渡的时间,通过 duration 属性

duration可以设置两种类型的值:

  • number 类型:同时设置进入和离开的过渡时间
  • object 类型:分别设置进入和离开的过渡时间
<template>
  <div>
    <button @click="isShow = !isShow">切换</button>
  </div>

  <!-- 设置 type 属性为 animation 或者 transition 来明确的告知Vue监听的类型 -->
  <!-- 通过 duration 属性,显示的来指定过渡的时间 -->
  <transition
    name="fade"
    type="transition"
    :duration="{ enter: 800, leave: 1000 }"  
  >
    <h2 class="title" v-if="isShow">
      它们是一片朦胧的温馨与寂寥,是一片成熟的希望与绝望,它们的领地只有两处:心与坟墓。比如说邮票,有些是用于寄信的,有些仅仅是为了收藏。
    </h2>
  </transition>
</template>

<script setup>
import { ref } from "vue";

const isShow = ref(true);
</script>

<style scoped>
.title {
  display: inline-block;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

.fade-enter-to,
.fade-leave-from {
  opacity: 1;
}

/* 过渡 */
.fade-enter-active,
.fade-leave-active {
  transition: opacity 1s ease;
}

/* 动画 */
.fade-enter-active {
  animation: anim 1s ease;
}
.fade-leave-active {
  animation: leaveAnim 1s ease;
}

/* 帧动画 */
/* 开始 */
@keyframes anim {
  0% {
    transform: scale(0);
  }

  50% {
    transform: scale(1.2);
  }

  100% {
    transform: scale(1);
  }
}
/* 离开 */
@keyframes leaveAnim {
  0% {
    transform: translateX(0);
  }

  100% {
    transform: translateX(-500px);
  }
}
</style>

5. 过渡的模式 mode

在两个元素之间切换的时候会存在问题(有卡顿),因为默认情况下进入和离开动画是同时发生的。

如果我们不希望同时执行进入和离开动画,需要设置transition的过渡模式:
  • in-out: 新元素先进行过渡,完成之后当前元素过渡离开
  • out-in: 当前元素先进行过渡,完成之后新元素过渡进入
<transition name="fade" mode="out-in">
  <h2 class="title" v-if="isShow">
    要是有些事我没说,地坛,你别以为是我忘了,我什么也没忘,但是有些事只适合收藏。不能说,也不能想,却又不能忘。
  </h2>
  <h2 class="title" v-else>
    它们不能变成语言,它们无法变成语言,一旦变成语言就不再是它们了。它们是一片朦胧的温馨与寂寥,是一片成熟的希望与绝望,它们的领地只有两处:心与坟墓。比如说邮票,有些是用于寄信的,有些仅仅是为了收藏。
  </h2>
</transition>

6. 动态组件的切换

首次渲染的时候是没有动画的,如果希望添加上去动画,那么就可以增加另外一个属性 appear(布尔值),默认为false

<transition name="fade" mode="out-in" :appear="true">
    <component :is="isShow ? Home : About"></component>
</transition>

二、结合animate.css库的使用

Animate.css是一个已经准备好的、跨平台的动画库为我们的web项目,对于强调、主页、滑动、注意力引导非常有用

1. 安装

npm install animate.css

2. 导入

import "animate.css";

3. 两种用法

🦋 用法一:直接使用animate库中定义的 keyframes 动画
<template>
  <div class="app">
    <button @click="isShow = !isShow">显式/隐藏</button>

    <transition name="fade">
      <h2 v-if="isShow">Hello Animate</h2>
    </transition>
  </div>
</template>

<script setup>
import { ref } from "vue";

const isShow = ref(true);
</script>

<style scoped>
.app {
  width: 300px;
  margin: 0 auto;
}

.title {
  display: inline-block;
}

.fade-enter-active {
  animation: bounceInLeft 1s ease-in;
}

.fade-leave-active {
  animation: bounceInRight 1s ease-in reverse;
}
</style>
🦋 用法二:直接使用animate库提供给的类(自定义过渡class)
<!--进入-->
enter-from-class
enter-active-class
enter-to-class
<!--离开-->
leave-from-class
leave-active-class
leave-to-class

他们的优先级高于普通的类名,这对于 Vue 的过渡系统和其他第三方 CSS 动画库,如 Animate.css. 结合使用十分有用。

<transition
  enter-active-class="animate__animated animate__fadeInDown"
  leave-active-class="animate__animated animate__flipInY"
>
  <h2 v-if="isShow">Hello Animate</h2>
</transition>

三、列表动画使用

1. 列表的过渡

目前为止,过渡动画主要是针对单个元素或者组件的:

  • 要么是单个节点
  • 要么是同一时间渲染多个节点中的一个

如果希望渲染的是一个列表(比如使用v-for),并且该列表中添加删除数据也希望有动画执行呢?

  • 这个时候我们要使用 <transition-group> 内置组件来完成

    html示例
    <input type="text" v-model="title" @keydown.enter="addTodo" />
    <ul v-if="todos.length">
          <transition-group name="flip-list" tag="ul">
            <li v-for="(todo, index) in todos" :key="todo.title">
              <input type="checkbox" v-model="todo.done" />
              <span :class="{ done: todo.done }"> {{ todo.title }}</span>
              <span  @click="removeTodo($event, index)"></span>
            </li>
          </transition-group>
    </ul>

    css示例

    .flip-list-enter-active,
    .flip-list-leave-active {
      transition: all 0.5s ease;
    }
    .flip-list-enter-from,
    .flip-list-leave-to {
      opacity: 0;
      transform: translateX(20px);
    }

    效果示例

  • 使用<transition-group> 有如下的特点:

    1. 默认情况下,它不会渲染一个元素的包裹器,但是可以指定一个元素并以 tag attribute 进行渲染;

    意思就是说,默认情况下<transition-group>在编译后,并不会生成一个真实的包裹元素;
    但是要想在编译后生成一个真实的包裹元素,我们可以通过设置“tag attribute”属性,就比如示例中设置了
    tag="ul"属性,在编译后就会生成一个ul标签包裹内部的li标签。

    2. 过渡模式不可用,因为不再相互切换特有的元素;

    3. 内部元素总是需要提供唯一的 key attribute 值;

    内部元素 总是需要 提供唯一的 key 属性值,不能使用index作为key,
    因为当你删除某项时,index会变化导致动画永远发生在最后一个元素身上,而不是当前元素!!!切记切记

    4. CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身;

    意思就是说,动画效果直接体现在内部的元素上,比如示例中的li。而不是<transition-group>上

2. 基本使用

🐡 1. 进入 / 离开动画
<template>
  <el-button @click="add">add</el-button>
  <el-button @click="remove">remove</el-button>
  <!-- 默认情况下, 不会渲染一个容器 DOM 元素,但是这里通过 tag = "div" 将 transition-group 渲染成了<div>。 -->
  <transition-group tag="div" class="list">
    <div v-for="item in list" class="item" :key="item">{{ item }}</div>
  </transition-group>
</template>
  
<script setup>
import { ref } from "vue";
const list = ref([1, 2, 3, 4, 5]);
const add = () => {
  list.value.push(Math.floor(Math.random() * 10) + 1);
};
const remove = () => {
  list.value.pop();
};
</script>
  
<style scoped lang="less">
.list {
  display: flex;
  width: 30px;
  flex-wrap: wrap;

  .item {
    width: 20px;
    margin: 5px;
    height: 20px;
    text-align: center;
  }
}

.list-enter-active,
.list-leave-active {
  transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
  opacity: 0;
  transform: translateX(30px);
}

.v-enter-active,
.v-leave-active {
  transition: all 0.5s ease;
}
.v-enter-from,
.v-leave-to {
  opacity: 0;
  transform: translateX(30px);
}
</style>

!!!注意,每个 transition-group 的子节点必须有独立的 key,动画才能正常工作。
🐡 2. 列表的移动过渡
transition-group” 组件还有一个特殊之处。除了进入和离开,它还可以为定位的改变添加动画。只需了解新增的 v-move 类就可以使用这个新功能,它会应用在元素改变定位的过程中。
像之前的类名一样,它的前缀可以通过 name attribute 来自定义,也可以通过 move-class attribute 手动设置“

上面的会有个问题,发现在随机移动的时候没有动画。

我们可以通过添加一些额外的 CSS 规则来解决这个问题:

.list-move, /* 对移动中的元素应用的过渡 */
.list-enter-active,
.list-leave-active {
  transition: all 0.5s ease;
}

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

/* 确保将离开的元素从布局流中删除
  以便能够正确地计算移动的动画。 */
.list-leave-active {
  position: absolute;
}

posted on 2024-12-02 11:21  梁飞宇  阅读(1064)  评论(0)    收藏  举报