Vue 2 进入、离开和列表过渡

前言

Vue 提供了多种方式来实现过渡效果。

  1. 在 CSS 过渡和动画中自动应用 class
  2. 配合 CSS 动画库
  3. 过渡钩子函数中使用 JavaScript 操作 DOM
  4. 配合 JavaScript 动画库

单元素/组件的过渡

将元素或组件放在 中可以在下列情形中触发过渡效果:

  1. 使用了 v-if
  2. 使用了 v-show
  3. 使用了动态组件
  4. 组件根节点

如果没有找到 JavaScript 过渡钩子和 CSS 过渡/动画,DOM 操作在下一帧中立即执行。


过渡的类名

Vue提供了 6 个可以自动生成的 CSS 类名,如下图。

Vue 2 进入、离开和列表过渡_第1张图片

可以自动生成的类名是指给 transition 组件的 name 属性指定一个值,假设是 fade,那么该 name 将自动扩展生成 .fade-enter、.fade-enter-active、.fade-enter-to等上述6个类,并应用在 transition 组件里的过渡元素或组件上。

示例:

DOCTYPE html>
<html>

<head>
    <title>Vue 2 过渡 Demotitle>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js">script>
    <style>
        .fade-enter {
            opacity: 0;
            transform: translateY(-20px);
        }

        .fade-enter-active {
            transition: all 0.3s ease;
        }

        .fade-enter-to {
            opacity: 1;
            transform: translateY(0);
        }

        .fade-leave {
            opacity: 1;
        }

        .fade-leave-active {
            transition: opacity 0.5s ease;
        }

        .fade-leave-to {
            opacity: 0;
        }
    style>
head>

<body>
    <div id="app">
        <button @click="showElement=!showElement">显示/隐藏元素button>
        <transition name="fade">
            <div v-if="showElement">
                你好
            div>
        transition>
    div>
    <script>
        new Vue({
            el: '#app',
            data: {
                showElement: false
            },
        });
    script>
body>

html>

Vue 2 进入、离开和列表过渡_第2张图片


自定义过渡类名

如果想结合使用 animate.css 动画库,可以在 transition 组件使用以下 props 来指定类名:

  • enter-class
  • enter-active-class
  • enter-to-class
  • leave-class
  • leave-active-class
  • leave-to-class

示例:

DOCTYPE html>
<html>

<head>
    <title>Vue 2 过渡 Demotitle>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js">script>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]" rel="stylesheet" type="text/css">
head>

<body>
    <div id="app">
        <button @click="showElement=!showElement">显示/隐藏元素button>
        <transition
            enter-active-class="animated fadeInLeft"
            leave-active-class="animated fadeOut">
            <div v-if="showElement">
                你好
            div>
        transition>
    div>
    <script>
        new Vue({
            el: '#app',
            data: {
                showElement: false
            },
        });
    script>
body>

html>

Vue 2 进入、离开和列表过渡_第3张图片

Animate 动画演示


指定要监听的事件类型

通过设置 transition 组件的 type 属性来指定要监听的过渡事件类型(transitionend 和 animationend)。

type 值可以是 transition 或 animation。

示例:

DOCTYPE html>
<html>

<head>
    <title>Vue 2 过渡 Demotitle>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js">script>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]" rel="stylesheet" type="text/css">
    <style>
        .fade-enter {
            opacity: 0;
            transform: translateY(-20px);
        }
    
        .fade-enter-active {
            transition: all 0.3s ease;
        }
    
        .fade-enter-to {
            opacity: 1;
            transform: translateY(0);
        }
    
        .fade-leave {
            opacity: 1;
        }
    
        .fade-leave-active {
            transition: opacity 0.5s ease;
        }
    
        .fade-leave-to {
            opacity: 0;
        }
    style>
head>

<body>
    <div id="app">
        <button @click="toggleElement">Toggle Elementbutton>
        <transition name="fade"
            type="transition"
            >
            <div v-if="showElement" key="element">Hello, Vue!div>
        transition>
        <transition name="bounce"
            enter-active-class="animated bounceIn"
            leave-active-class="animated bounceOut"
            type="animation"
            >
            <div v-if="showElement" key="element">Hello, vue!div>
        transition>
    div>
    <script>
        new Vue({
            el: '#app',
            data: {
                showElement: false
            },
            methods: {
                toggleElement() {
                    this.showElement = !this.showElement;
                },
            },
        });
    script>
body>

html>

Vue 2 进入、离开和列表过渡_第4张图片


指定过渡持续时间

默认情况下,Vue 会等待过渡所在根元素的第一个 transitionend 或 animationend 事件。

我们可以使用 transition 组件上的 duration prop 定制一个显示的过渡持续时间,以毫秒为单位。

<transition :duration="1000">...transition>

<transition :duration="{ enter: 500, leave: 800 }">...transition>

transition 组件上的 JavaScript 钩子函数

共 8 个:

  • before-enter
  • enter
  • after-enter
  • enter-cancelled
  • before-leave
  • leave
  • after-leave
  • leave-cancelled

注意点:

  • 只用 JavaScript 过渡时,在 leave 和 enter 中必须使用 done 参数来告诉 Vue何时过渡已经完成。
  • 推荐对于仅使用 JavaScript 过渡的元素添加 v-bind:css="false",Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。
DOCTYPE html>
<html>

<head>
    <title>Vue 2 过渡 Demotitle>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js">script>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]" rel="stylesheet" type="text/css">
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js">script>
    style>
head>

<body>
    <div id="app">
        <button @click="toggleElement">切换元素button>
        <transition name="custom-transition" @before-enter="beforeEnter" @enter="enter" @leave="leave">
            <div v-if="showElement" key="element">Hello, Vue!div>
        transition>
    div>
    <script>
        new Vue({
            el: '#app',
            data: {
                showElement: false
            },
            methods: {
                toggleElement() {
                    this.showElement = !this.showElement;
                },
                beforeEnter(el) {
                    // 在进入过渡开始之前调用
                    el.style.opacity = 0;
                },
                enter(el, done) {
                    // 在进入过渡的主要阶段调用
                    Velocity(el, { opacity: 1, translateY: '0px',fontSize: '2em' }, { duration: 1000, complete: done });
                    Velocity(el, { fontSize: '1em' }, { complete: done })
                },
                leave(el, done) {
                    // 在离开过渡的主要阶段调用
                    Velocity(el, { opacity: 0, translateY: '20px' }, { duration: 1000, complete: done });
                },
            }
        });
    script>
body>

html>

Vue 2 进入、离开和列表过渡_第5张图片


初始渲染的过渡

启用初始渲染的过渡:

<transition
	appear
	appear-class=""
	appear-active-class=""
	appear-to-class=""
	v-on:before-appear=""  
	v-on:appear=""  
	v-on:after-appear=""  
	v-on:appear-cancelled=""
	>
transition>

多个元素的过渡

如果想实现多个原生元素的切换过渡,可以使用条件渲染的相关指令。

如果切换的元素标签名相同,比如都是 button ,那么最好给它们设置唯一的key属性,否则可能出现过渡效果不生效或没按设置过渡的情况。

DOCTYPE html>
<html>

<head>
    <title>Vue 2 过渡 Demotitle>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js">script>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]" rel="stylesheet" type="text/css">
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js">script>
    style>
head>

<body>
    <div id="app">
        <button @click="toggleElement">切换元素button>
        <transition @before-enter="beforeEnter" @enter="enter" @leave="leave">
            <div v-if="showElement" key="el1">Hello, girls!div>
            <div v-else key="el2">Hello, boys!div>
        transition>
    div>
    <script>
        new Vue({
            el: '#app',
            data: {
                showElement: false
            },
            methods: {
                toggleElement() {
                    this.showElement = !this.showElement;
                },
                beforeEnter(el) {
                    // 在进入过渡开始之前调用
                    el.style.opacity = 0;
                },
                enter(el, done) {
                    // 在进入过渡的主要阶段调用
                    Velocity(el, { opacity: 1, translateY: '0px',fontSize: '2em' }, { duration: 1000, complete: done });
                    Velocity(el, { fontSize: '1em' }, { complete: done })
                },
                leave(el, done) {
                    // 在离开过渡的主要阶段调用
                    Velocity(el, { opacity: 0, translateY: '20px' }, { duration: 1000, complete: done });
                },
            }
        });
    script>
body>

html>

Vue 2 进入、离开和列表过渡_第6张图片

也可以使用绑定动态 property 的方式来替代使用了多个 v-if 的相同标签名元素

<transition>
	<div v-if="show==='el1'" key="el1">el1div>
	<div v-if="show==='el2'" key="el2">el2div>
transition>

替换成

<transition>
	<div :key="el">eldiv>
transition>

过渡模式

Vue 2 进入、离开和列表过渡_第7张图片

如上图中,前一个元素的过渡结束时,另一个元素的过渡开始,这有时候并不是我们想要的效果。

于是,Vue 提供了过渡模式。

<transition name="fade" mode="out-in">
transition>

mode 可以为以下值:

  • in-out:新元素先过渡进入,旧元素后过渡离开。
    Vue 2 进入、离开和列表过渡_第8张图片
  • out-in:旧元素先过渡离开,新元素再过渡进入。
    Vue 2 进入、离开和列表过渡_第9张图片

So cool!


多个组件的过渡

多个元素我们可以使用 key attribute,对于多个组件,我们可以使用更为简单的 is attribute,即动态组件。

<transition name="component-fade" mode="out-in">
  <component v-bind:is="view">component>
transition>

列表过渡

前面所讲都是针对单个节点或者多个节点中的一个。

而对于有多个节点的列表的渲染,我们使用 transition-group 组件。

transition-group 组件的特点:

  1. transition 不同,它以一个真实的元素存在,默认为 span,可以使用 tag attribute 修改。
  2. 不能使用过渡模式。
  3. 列表元素都需要 key attribute。
  4. CSS 过渡的类会自动应用在列表内部元素。

列表的进入/离开过渡

DOCTYPE html>
<html>

<head>
    <title>Vue 2 过渡 Demotitle>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js">script>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]" rel="stylesheet" type="text/css">
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js">script>
    style>
    <style>
        .list-enter-active,
        .list-leave-active {
            transition: opacity,transform 0.5s;
        }

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

        .list-item {
            list-style:decimal-leading-zero;
            margin: 5px;
            padding: 5px;
            background-color: #f0f0f0;
            border: 1px solid #ddd;
            border-radius: 5px;
            width: fit-content;
        }
    style>
head>

<body>
    <div id="app">
        <button @click="addItem">Add Itembutton>
        <button @click="removeItem">Remove Itembutton>
        <transition-group name="list" tag="ul">
            <li v-for="item in items" :key="item.id" class="list-item">
                {{item.text}}
            li>
        transition-group>
    div>
    <script>
        new Vue({
            el: '#app',
            data: {
                items: [],
                nextItemId: 0
            },
            methods: {
                addItem() {
                    let randomIndex=Math.floor(Math.random()*this.items.length);
                    this.items.splice(randomIndex,0, {
                        id: this.nextItemId++,
                        text: `Item ${this.nextItemId}`
                    });
                },
                removeItem() {
                    let randomIndex = Math.floor(Math.random()*this.items.length);
                    this.items.splice(randomIndex, 1);
                }
            },
        });
    script>
body>

html>

Vue 2 进入、离开和列表过渡_第10张图片


列表的排序过渡

不仅可以给进入/离开添加过渡,当列表的元素位置发生变化时,也可以产生过渡效果,通过 move-class attribute自定义类名来实现,也可以使用 name 作为前缀。

例如当 name="list" 时:

.list-move {
  transition: transform 1s;
}

这个可以解决列表过渡不够平滑的问题。

如果想给所有的变动都添加过渡动画,那么可以按以下方法做:

DOCTYPE html>
<html>

<head>
    <title>Vue 2 过渡 Demotitle>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js">script>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]" rel="stylesheet" type="text/css">
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js">script>
    style>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js">script>
    <style>
        .list-enter,
        .list-leave-to {
            opacity: 0;
            transform: translateX(30px);
        }

        .list-leave-active{
            position: absolute;
        }

        .list-item {
            transition: all 1s;
            list-style: none;
            margin: 5px;
            padding: 5px;
            background-color: #f0f0f0;
            border: 1px solid #ddd;
            border-radius: 5px;
            width: fit-content;
        }
    style>
head>

<body>
    <div id="app">
        <button @click="addItem">Add Itembutton>
        <button @click="removeItem">Remove Itembutton>
        <button @click="shuffleItems">Shuffle Itemsbutton>
        <transition-group name="list" tag="ul">
            <li v-for="item in items" :key="item.id" class="list-item">
                {{item.text}}
            li>
        transition-group>
    div>
    <script>
        new Vue({
            el: '#app',
            data: {
                items: [],
                nextItemId: 0
            },
            methods: {
                addItem() {
                    let randomIndex = Math.floor(Math.random() * this.items.length);
                    this.items.splice(randomIndex, 0, {
                        id: this.nextItemId++,
                        text: `Item ${this.nextItemId}`
                    });
                },
                removeItem() {
                    let randomIndex = Math.floor(Math.random() * this.items.length);
                    this.items.splice(randomIndex, 1);
                },
                shuffleItems() {
                    this.items = _.shuffle(this.items);
                }
            },
        });
    script>
body>

html>

Vue 2 进入、离开和列表过渡_第11张图片

这被叫做  FLIP 过渡,使用这种过渡方式的元素不能设置为 display:inline


列表的交错过渡

通过 HTML 的 data 属性与 JavaScript 进行通信来实现交错排序。

<li  
v-for="(item, index) in computedList"  
v-bind:key="item.msg"  
v-bind:data-index="index"  
>{{ item.msg }}li>
enter: function (el, done) {
  var delay = el.dataset.index * 150; // 通过 data-index 获取元素的索引,然后计算延迟时间
  setTimeout(function () {
    Velocity(
      el,
      { opacity: 1, height: '1.6em' }, // 使用 Velocity.js 库来应用 CSS 属性的动画效果
      { complete: done } // 动画完成后调用 done 回调函数来通知 Vue.js 过渡完成
    );
  }, delay);
},

每个元素都有不同的延迟,以实现交错的过渡效果。

Vue 2 进入、离开和列表过渡_第12张图片


可复用的过渡

使用 template :

Vue.component('my-special-transition', {
  template: '\
    \
      \
    \
  ',
  methods: {
    beforeEnter: function (el) {
      // ...
    },
    afterEnter: function (el) {
      // ...
    }
  }
})

使用 函数式组件 :

Vue.component('reusable-transition', {
  functional: true,
  render: function (createElement, context) {
    var data = {
      props: {
        name: 'reusable-transition',
        mode: 'out-in'
      },
      on: {
        beforeEnter: function (el) {
          // ...
        },
        afterEnter: function (el) {
          // ...
        }
      }
    }
    return createElement('transition', data, context.children)
  }
})

使用 单文件组件:






总之,可复用过渡就是以 transitiontransition-group 作为根元素。


动态过渡

我们可以根据组件的状态或属性动态地应用不同的过渡效果。

通常涉及使用过渡的不同名称、条件语句和组件的动态属性。

DOCTYPE html>
<html>

<head>
    <title>Vue 2 过渡 Demotitle>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js">script>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]" rel="stylesheet" type="text/css">
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js">script>
    <style>
        .default-enter,
        .default-leave-to {
            opacity: 0;
            transform: translateX(30px);
        }

        .slide-enter,
        .slide-leave-to {
            transform: translateY(30px);
            opacity: 0;
        }

        .default-leave-active,
        .slide-leave-active{
            position: absolute;
        }

        .list-item {
            transition: all 1s;
        }
    style>
head>

<body>
    <div id="app">
        <input v-model="newTodo" @keyup.enter="addTodo" placeholder="添加新任务">
        <button @click="toggleTransition">切换过渡方式button>
        <transition-group :name="dynamicTransitionName" tag="ul">
            <li v-for="todo in todos" :key="todo.id" class="list-item">
                {{todo.text}}
                <button @click="removeTodo(index)">删除button>
            li>
        transition-group>
    div>
    <script>
        new Vue({
            el: '#app',
            data: {
                dynamicTransitionName: 'default',
                newTodo: '',
                todos: [],
                nextID: 0
            },
            methods: {
                addTodo() {
                    if (this.newTodo.trim() !== '') {
                        this.todos.push({id:this.nextID++, text: this.newTodo });
                        this.newTodo = '';
                    }
                },
                removeTodo(index) {
                    this.todos.splice(index, 1);
                },
                toggleTransition(){
                    this.dynamicTransitionName=this.dynamicTransitionName==='default'?'slide':'default';
                }
            },
        });
    script>
body>

html>

Vue 2 进入、离开和列表过渡_第13张图片

你可能感兴趣的:(Vue,vue.js,前端,javascript)