# 大纲

  • Vue动画方式 1 - CSS transition
  • Vue动画方式 2 - CSS animation
  • Vue动画方式 3 - JS 操作动画
  • Vue动画方式 4 - 多元素动画
  • Vue动画方式 5 - 列表动画
  • Vue动画方式 6 - 组件过渡动效
  • 总结之前的几种动画库
  • 项目运用

首先了解 Vue文档中的 进入/离开 & 列表过渡 (opens new window)

  • Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。包括以下工具:
    • 在 CSS 过渡动画中自动应用 class
    • 可以配合使用第三方 CSS 动画库,如 Animate.css
    • 过渡钩子函数中使用 JavaScript 直接操作 DOM
    • 可以配合使用第三方 JavaScript 动画库,如 Velocity.js(dead) anime.js GSAP(Vue3.0推荐)

# Vue动画方式 1 - CSS transition

  • Vue 提供封装的<transition name="xxx"></transition>标签,在标签内写需要设置动画的节点
  • 给任何元素/组件 添加 进入/离开 过渡效果
    • 条件渲染v-if
    • 条件展示v-show
    • 动态组件v-bind:is=""
    • 组件根节点
  • 必须设置相关类样式的前缀name="xxx",必须包括如下样式:
    • xxx-enter-active
    • xxx-leave-active
    • xxx-enter
    • xxx-leave-to
  • 注意 进入/离开 过渡样式相同时,可以并列(例如设置name="fade"
    • .fade-enter-active, .fade-leave-active {}
    • .fade-enter, .fade-leave-to {}
  • 一些 UI 组件库的CSS全局样式会污染默认样式

例子


CSS transition


分析 1

  • <transition></transition>包裹需要加动画的节点
  • 设置属性name: <transition name="fade">
  • 设置初始样式,比如
    • 无自身初始值的属性,比如width 必须有初始值
    • opacity 无需设置初始值,初始即为1
  • 设置过度样式
    • .fade-enter-active, .fade-leave-active {transition: all 2s;}
    • .fade-enter, .fade-leave-to {...}
  • 设置 切换条件 v-if / v-show

分析 2

  • 过渡过程name属性值替代v
    • 使用一个没有名字的 <transition>,则 v- 是这些类名的默认前缀
    • 如果使用了 <transition name="my-transition">,那么 v-enter 会替换为 my-transition-enter
  • 淡入的第一帧 .v-enter 元素插入前生效的样式,插入后下一帧删除
  • 进入过度生效时状态 .v-enter-active 过渡/动画.v-active {transition: all 2s;} 此时使用transition过渡
  • 进入过度结束状态.v-enter-to 淡入最后一帧 可添加样式
  • 一般不需要.v-enter-to.v-leave,结束状态即初始状态
  • 完成过渡,删除所有过渡v的样式,只保留元素自身原来的样式
  • v-leave -> v-leave-active -> v-leave-to 离开过渡
  • .v-enter-active v-leave-active过渡/动画 完成之后移除
    • 这个类可以被用来定义离开过渡的过程时间 duration,延迟 delay和曲线函数easing-function
  • v-enter-to v-leave-to 在 **过渡/动画 **完成之后移除

v3

CSS transition 过渡 另一个例子 Slide

封装通用组件ShowPanel

<template>
  <div>
    <br>
    <h2>{{ title }}</h2>
    <br>
    <button @click="toggle">
      {{buttonText}}
    </button>
    <transition :name="transitionName"
                v-if="animationMode"
    >
      <p class="animateTarget" v-show="visible">{{ showText }}</p>
    </transition>
    <transition v-else
                :name="transitionName"
                :enter-active-class="animeEnterClassName"
                :leave-active-class="animeLeaveClassName"
    >
      <p class="animateTarget" v-show="visible">{{ showText }}</p>
    </transition>
    <br>
  </div>
</template>

<script>
export default {
  data() {
    return {
      visible: true
    }
  },
  methods: {
    toggle() {
      this.visible = !this.visible
    }
  },
  props: {
    title: {
      type: String,
      required: true,
      default: '',
    },
    transitionName: {
      type: String,
    },
    showText: {
      type: String,
      required: true,
      default: 'Hello',
    },
    buttonText: {
      type: String,
      default: 'Toggle',
    },
    animationMode: {
      type: Boolean,
      default: true,
    },
    animeEnterClassName: {
      type: String,
      default: '',
    },
    animeLeaveClassName: {
      type: String,
      default: '',
    }
  }
}
</script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

CSS transition


小结


codepen可以快速尝试适合的依赖环境


# Vue动画方式2- CSS animation

# 使用animation属性和@keyframes关键帧动画

使用方法

  • 同样使用<transition name="xxx"> 标签,name="xxx"为类样式前缀
  • 区别于CSS transition 过渡属性:
    • CSS animation 动画属性 v-enter 类名在节点插入 DOM 后不会立即删除
    • 默认情况下,Vue 会等待过渡所在根元素的第一个 transitionend 或 animationend 事件
    • v-enter 类在 animationend 事件 (MDN) 被触发时删除
    • animationend 事件 (MDN) (opens new window)
  • @keyframes各个关键帧中设置 变形、颜色、透明等属性
    • 需要设置过渡状态的类样式
    • .xxx-enter-active{animation: ...}:正在淡入
    • .xxx-leave-active{animation: ...}:正在淡出
    • @keyframes中 设置关键帧(to / from / 0% / 100%)代替enter / leave
    • reverse 反向淡出动画,设置.xxx-leave.active{animation: animationName 1s reverse}
    • 保证最终状态即正常状态

animation属性和@keyframes关键帧动画示例


CSS animation


# 自定义过渡类名

在以下的<trasition>标签属性中可添加自定义过渡类名

  • enter-class
  • enter-active-class
  • enter-to-class (2.1.8+)
  • leave-class
  • leave-active-class
  • leave-to-class (2.1.8+)

例如:

  • <transition enter-active-class="xxx" leave-active-class="yyy">...</transition>

# 结合animate.css动画库使用

测试支持的版本

  • codepen中,直接选取依赖版本尝试运行是否成功
  • 如果是本地项目
    • 先在BootCDN找到库引用,选择版本,直接插入项目的index.html
    • 尝试是否可以运行
    • 将可以正常运行的版本号作为yarn add -D xxx@***的依据
    • 导入入口文件import ...*.js/ts

Vue2.x运行环境(经过在codepen中的测试)

  • @vue/cli-plugin-babel@4.1.1
  • animate.css@3.6.1animate.css@4.1.1
  • vue@2.6.11

或者Vue3.x运行环境

  • @vue/cli-plugin-babel@4.5.12
  • animate.css@4.1.1
  • vue@3.0.11

安装animate.css

yarn add animate.css@3.6.1
# yarn add animate.css@4.1.1
1
2

全局引入:在入口文件main.ts中导入 (或者在需要用到的vue组件中局部引入)

import 'animate.css'
...
1
2

使用animate.css

  • 注意使用时animate.css@3.6.1animate.css@4.1.1类前缀不同
  • 导入到入口文件main.js/ts中去:import 'animate.css'
  • 编译后会自动添加相应的<style></style>标签为全局样式

animate.css示例


CSS animate.css@3.6.1


  • CSS 动画样式在第三方库中已预设
  • 添加属性enter-active-class leave-active-class
    • <transition enter-active-class="animated tada" leave-active-class="animated bounceOutRight">
  • Animate Css官网 (opens new window)

自定义过渡类名配合使用animate.css和使用animation属性和@keyframes关键帧动画不兼容


# Vue动画方式 3 - JS 操作动画

  • attribute (横线连接 (kebab-case)) 中声明 JavaScript 钩子(驼峰式连接)
  • methods中写对应的方法
<transition
  @before-enter="beforeEnter"
  @enter="enter"
  @after-enter="afterEnter"
  @enter-cancelled="enterCancelled"
  @before-leave="beforeLeave"
  @leave="leave"
  @after-leave="afterLeave"
  @leave-cancelled="leaveCancelled">
  <!-- ... -->
</transition>
1
2
3
4
5
6
7
8
9
10
11
  • 动画的生命周期
    • @before-enter @enter @after-enter @enter-cancelled
    • @before-leave @leave @after-leave @leave-cancelled

# 不推荐Velocity.js(译为:速度) 动画库(github REPO已停止维护)

安装稳定版

yarn add velocity-animate
1

使用略


# Vue配合动画库anime.js 版本animejs@3.2.1

安装

yarn add animejs@3.2.1 --save
1
  • 安装 "animejs": "^3.2.1"(如果装TS 还需安装对应类型"@types/animejs": "^3.1.3")

官方链接

参考文章

temp link

示例


# 配合动画库GSAP(Vue3.x推荐)


# Vue动画方式 4 - 多元素动画

示例

<transition>
  <article v-if="items.length > 0">
    <!-- 多标签过渡是一个列表和描述这个列表为空消息的元素 -->
  </article>
  <p v-else>Sorry, no items found.</p>
</transition>

<!-- 相同标签名的元素切换 -->
<transition>
  <button v-if="isEditing" key="save">
    Save
  </button>
  <button v-else key="edit">
    Edit
  </button>
</transition>

<!-- d多个标签名的元素切换 -->
<transition>
  <button v-if="docState === 'saved'" key="saved">
    Edit
  </button>
  <button v-if="docState === 'edited'" key="edited">
    Save
  </button>
  <button v-if="docState === 'editing'" key="editing">
    Cancel
  </button>
</transition>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  • 写在<transition></transition>标签上的属性 过渡模式 mode="out-in",先退出,再进入
  • <transition></transition>标签中包含 原生标签
    • 对于不同标签名的元素切换
      • 使用 v-if/v-else
    • 对于相同标签名的元素切换
      • 必须通过 key attribute 设置唯一的值来标记以让 Vue 区分
      • 多个相同结构与属性的元素进行合并,使用动态绑定:key区分,代替v-if/v-else
  • 如果使用 过渡模式 mode="in-out",需要将元素设置为绝对定位 position: absolute;
  • 轮播效果无需设置过渡模式

# 元素切换实例

切换两个元素


切换三个元素


切换三个元素 mode: in-out

/docs/.vuepress/mixins/MultiElements.js

import MultiElementTemplate from "../components/Vue/Animation/MultiElementTemplate.vue";
export default {
    components: {MultiElementTemplate},
    data() {
        return {
            docStateList: ['saved', 'edited', 'editing'],
            docState: 'saved',
            tempList: [],
            stateCount: 0
        }
    },
    computed: {
        buttonMessage: {
            get: function () {
                return {
                    'saved': 'Edit',
                    'edited': 'Save',
                    'editing': 'Cancel',
                }[this.docState]
            },
            set: function (state) {
                this.docState = state
            }
        }
    },
    beforeMount() {
        this.tempList = [...this.docStateList]
    },
    methods: {
        switchingDocState() {
            this.docState = this.tempList.pop();
            if (this.tempList.length === 0) {
                this.tempList = [...this.docStateList]
            }
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

切换三个元素 Carousel with no mode

  • 注意:不要在<transition>标签内写JS行内表达式,而是要写到方法methods
  • 否则会引起无线循环渲染报错[Vue warn]: You may have an infinite update loop in a component render function

# 多个组件的过渡

<transition name="component-fade" mode="out-in">
  <component :is="view"></component>
</transition>
1
2
3

示例


<template>
  <div>
    <button @click="view='AnimeJS'">AnimeJS</button>
    <button @click="view='Slide'">Slide</button>
    <transition name="component-fade" mode="out-in">
      <component :is="view"></component>
    </transition>
  </div>
</template>

<script>
import AnimeJS from "./AnimeJS.vue";
import Slide from "./Slide.vue";

export default {
  name: "MultiComponents",
  data() {
    return {
      view: 'AnimeJS'
    }
  },
  components: {
    AnimeJS,
    Slide
  }
}
</script>

<style lang="scss" scoped>
.component-fade-enter-active,
.component-fade-leave-active {
  transition: opacity .3s ease;
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

# Vue动画方式 5 - 列表动画 <transition-group>

TIP

  • 除了渲染单个节点或者同一时间渲染多个节点中的一个Vue 还能同时渲染整个列表
<div>
  <button @click="add">Add</button>
  <button @click="remove">Remove</button>
  <transition-group name="list" tag="p">
    <span v-for="item in items" :key="item">
      {{ item }}
    </span>
  </transition-group>
</div>
1
2
3
4
5
6
7
8
9
  • 利用<transition-group></transition-group>可以对v-for渲染的每个元素应用过渡效果

TIP

  • 由于循环渲染的多个元素必须放在一个标签中
    • 直接在<transition-group></transition-group>中写一个包裹元素
    • 则子元素不是循环渲染的元素,无法识别
  • vue使用tag attribute,编译时将<transition-group tag="xxx">替换为设置的元素名xxx
  • 不同于 <transition>,用<transition-group></transition-group>会以一个真实元素呈现:
    • 默认为一个 <span>
    • 可以通过 tag attribute 更换为其他元素
  • 过渡模式mode attribute不可用,因为不再相互切换特有的单个元素
  • 内部元素必须提供唯一的 key attribute
  • CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身

# 列表的进入/离开过渡(enter/leave)

示例


TIP

  • 数据data里不能循环引用和写入Vue实例
...
// error
data() {
  return {
    items: [1, 2, 3, 4, 5],
    nextNum: this.items
    }
}
1
2
3
4
5
6
7
8

解决当 添加和移除 元素时 平滑过渡 的问题

  • 当添加和移除元素的时候,周围的元素会瞬间移动到他们的新布局的位置,而不是平滑的过渡
  • 设置<transition-group name="xxx">xxx-move {}xxx-leave-active {} 类的样式可以实现平滑过渡
    • xxx-move {transition: all 1s;}
    • xxx-leave-active {position: absolute;}

# 列表的移动过渡(move)

  • 使用 v-move class(其中的v 是用置<transition-group name="xxx">中的属性name代替)
    • 不仅可以进入和离开动画,还可以改变定位,即为在元素的改变定位的过程中应用
    • 设置 过渡的切换时机过渡曲线 的例子
  • 可以通过 name attribute 来自定义前缀
  • 也可以通过 move-class attribute 手动设置

示例

  • 安装yarn add lodash
  • 引入import _ from 'lodash';
  • 示例中为列表设置了排序时移动的过渡

  • 内部的实现:Vue 使用了一个叫 FLIP (opens new window) 简单的动画队列
  • 使用 transforms 将元素从之前的位置平滑过渡新的位置
  • 注意使用 FLIP 过渡的元素不能设置为 display: inline
  • 作为替代方案,可以设置为 display: inline-block 或者放置于 flex

综合示例 使列表的一切变动都会有动画过渡

  • 封装了.../mixins/random.js的逻辑
export default {
  methods: {
    randomIndex() {
      return Math.floor(Math.random() * this.itemList.length)
    },
    add() {
      this.itemList.splice(this.randomIndex(), 0, this.nextNum++)
    },
    remove() {
      this.itemList.splice(this.randomIndex(), 1)
    },
    recover() {
      this.itemList = [...this.originItems]
    },
    oddEven() {
      ((10 - Math.floor(Math.random() * 10)) % 2 === 0) ?
        (this.add()) :
        (this.remove())
    },
    checkItemList() {
      if (this.itemList.length === 0) {
        this.itemList.push(0)
        this.recover()
      }
    },
  },
  watch: {
    itemList: {
      handler: 'checkItemList',
    }
  },
  created() {
    this.originItems = [...this.itemList]
  },
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 列表<transition-group>的多维网格过渡 Grid

示例


# 列表<transition-group>的交错过渡 Staggered

示例


# 列表动画小结


# <transition><transition-group>的可复用的过渡

过渡可以通过 Vue 的组件系统实现复用。要创建一个可复用过渡组件:

  • 直接将<transition><transition-group>作为根组件,加上方法或样式,进行统一封装
  • 使用插槽<slot></slot>,使用时将任何子组件放置在其中
<template>
  <transition
          name="very-special-transition"
          mode="out-in"
          @before-enter="beforeEnter"
          @after-enter="afterEnter">
    <slot></slot>
  </transition>
</template>
<script>
export default {
  methods: {
    beforeEnter(el) { // ... },
    afterEnter(el) { // ... }
  }
}
</script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# <transition><transition-group>的动态过渡

  • 过渡也是数据驱动
  • 动态过渡最基本的例子是通过 name attribute 来绑定动态值
  • 用 Vue 的过渡系统来定义的 CSS 过渡/动画在 不同 过渡间 切换
<transition :name="transitionName">
  <!-- ... -->
</transition>
1
2
3
  • 所有过渡 attribute 都可以动态绑定,比如move attribute
  • 还可以通过事件钩子获取上下文中的所有数据,因为事件钩子都是方法
  • 根据组件的状态不同,JS 过渡 会有不同的表现

示例


  • 创建动态过渡的最终方案是组件通过接受 props 来动态修改之前的过渡

---

## 总结之前的几种动画

---

## 动画库

### 第三方 CSS 动画库[`Animate.css`](https://www.dowebok.com/demo/2014/98/)

- 安装`yarn add animate.css@3.6.1`

```html
<template>
<div>
  <button @click="showAnimateCSS = !showAnimateCSS">
    Toggle render
  </button>
  <transition name="custom-classes-transition"
    enter-active-class="animated tada"
    leave-active-class="animated bounceOutRight">
    <p v-if="showAnimateCSS">hello</p>
  </transition>
</div>
</template>

<script>
export default {
  data() {
    return {
      showAnimateCSS: true
    }
  },
}
</script>

<style lang="scss" scoped>
// ...
</style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  • CSS在第三方库中预设好了
  • 添加属性enter-active-class leave-active-class
    • <transition enter-active-class="animated tada" leave-active-class="animated bounceOutRight">
  • animate.css 官网 (opens new window)

方案导向


# 项目运用

  • Tab切换
  • 轮播
  • 跳动的心动画

参考



Last Updated: 2022/1/18 上午6:09:16