Vue 2.0学习笔记:Vue的transition

动效在Web中一直是一个有争议的问题。动效做得好有助于在你的Web程序上锦上添花,甚至是留住你的用户,也可以具有较好的用户体验;反之,如果动效运用的不好,会给用户带来一种反感,让用户迅速地离开你的应用。怎么提供更友好的动效,并不是今天我们要讨论的重点,我们要讨论的是:在Vue应用程序中如何添加动效?在Vue中,提供了多种方法来给你的运用程序添加动效,比如CSS的transitionanimation动效,以及在Vue的生命周期的钩子函数中操作DOM。甚至你还要以使用第三方动画库,比如GSAPVelocity.js来制作动效。

在本文中,我们将先重点了解Vue中处理CSS的transition的原理。有了这些知识,就可以开始创建自己的过渡动效。一旦掌握了这些基础知识,就可以快速掌握Vue中的transitionanimation的全部功能。

transition vs animation

在具体了解Vue中的transition之前,咱们先简单的了来了解一下transitionanimation之间的差异(这里所指的是CSS中两者的差异)。先上一张录制的动图:

Vue 2.0学习笔记:Vue的transition

两者的效果就如下图所示这样:

Vue 2.0学习笔记:Vue的transition

如果用代码来描述的话:

/* transition*/
.button {
    background: blue;
    transition: background;

    &:hover {
        background: red;
    }
}

/* animation */
@keyframe spin {
    0% {
        color: red;
    }
    50% {
        color: blue;
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }
}

.element {
    animation: spin 1s ease-in-out 2s infinite;
}

也就是说,transition是指一个元素从状态A状态B,即开始状态是A,结束状态是B。而其主要的目的是创建一个自然的演示,告诉用户的交互行为已经发生了更改(就是从A变成了B)。在现实的Web应用中这样的示例也非常的多。比如下拉菜单的效果就是其中的一个典型案例。默认情况下,下拉菜单是收缩闭合的(隐藏不见,就是我们所说的状态A),当用户点击了或者鼠标悬浮在菜单项上,下拉菜单就会展开(下拉菜单可见,就是我们所说的状态B)。这样的一个过程如果添加了transition的话,就自钱的完成了闭合(隐藏)自动过渡到展开(可见)状态。比如下图所示:

Vue 2.0学习笔记:Vue的transition

animationtransition有很大的不同之处,从状态A到状态B之间有很多中间态,而这些被称为关键帧。因此,animation也常常被称为帧动画,也有人称为补间动画。比如下图所示:

Vue 2.0学习笔记:Vue的transition

从上图我们也可以看出来,animation有很多中间态,即,可以从AB,再到C,再到D,甚至更多。其主要目的是不断展示某些东西正在改变。它们可以有结束状态,但与transition不同,它们不限于两种状态。比如下面这样的一个示例,它不断地从一种状态变化到另一种状态,但它最终可能会结束。

Vue 2.0学习笔记:Vue的transition

在某种程度上,animation只是transition的一个超级集合,因为它添加了更多的中间状态。虽然transition只是从状态A到状态B,但animation可以根据需要拥有任意多的中间状态。也就是说,只要理解了transition的基本原理之后,在去理解animation就不是什么一件难事了。这也就是我们今天为什么把重点放在transition`上。

transition元素

在Vue中有一个<transition>元素(即一个容器),它主要用来处理元素或组件上的transition动效,CSS和JavaScript的animation动效,而且会让你处理这些动效变得简地多。而在CSS的transition动效中,<transition>元素主要负责应用和取消类(元素的类名)。而你所要做的就是定义元素在transition动效期间元素的样式。

<transition>在Vue中使用非常的简单,把要带动效的元素放到这个容器之中,同时使用name给其一个名称,比如fade

<transition name="fade">
    <h1 v-if="show">Hello! W3cplus.com (^_^)</h1>
</transtion>

当在<transition>容器中的元素在显示(插入)或隐藏(删除)时,Vue会自动嗅探到目标元素是否应用了CSS的transitionanimation,如果是,在恰当的时机添加或删除CSS类名。

简单地说:

<transition>是Vue已经封装好的一个组件,可以给任何元素和组件添加进入或离开过渡效果。

在Vue中的下面这几种情形会产生相应的过渡或动画效果:

  • 条件渲染(使用v-if
  • 条件显示(使用v-show
  • 动态组件
  • 组件根节点

比如下面这个示例:

<!-- FadeText -->
<template>
    <div class="wrapper">
        <transition name="fade">
            <h1 v-if="isShow">{{ msg }}</h1>
        </transition>
        <button @click="isToggle" :class="isShow ? 'is-show' : 'is-hidden'">
            {{ isShow ? "隐藏" : "显示" }}
        </button>
    </div>
</template>

<script>
export default {
    name: "FadeText",
    data() {
        return {
            isShow: true,
            msg: "Hello! W3cplus.com (^_^)"
        };
    },
    methods: {
        isToggle() {
            this.isShow = !this.isShow;
        }
    }
};
</script>

<style scoped>
    .fade-enter-active,
    .fade-leave-active {
        transition: opacity 0.5s;
    }
    .fade-enter,
    .fade-leave-to {
        opacity: 0;
    }
</style>

效果如下:

上面这个简单的过渡效果使用的是CSS的transition方式来实现。那么v-if在切换元素h1的时候发生了什么呢?

  • v-if绑定的值isShowtruefalse之间切换的时候,<transition>中的元素<h1>会插入和删除之间进行切换
  • 自动嗅探目标元素h1是否应用了CSS的transitionanimation。如果是,则会在元素插入时添加CSS类名,并判断动画加载完之后删除CSS类名
  • 如果过渡组件提供了JavaScript钩子函数(这部分后面会介绍),这些钩子函数在恰当的时机被调用。在这个示例中并没有用到JavaScript钩子函数,所以不会被执行
  • 如果没有找到JavaScript钩子并且也没有检测到CSS的transitionanimation,DOM操作(插入或删除)在下帧中立即执行。(注意:此指浏览器逐帧动画机制,和 Vue 的 nextTick 概念不同)

这个时候,通过浏览器的动画(Animations)选项可以看到动效的整个过程,如下图所示:

Vue 2.0学习笔记:Vue的transition

过渡的类名

在Vue中使用<transition>制作过渡效果时,可以给过渡元素添加类名,在Vue中有六个类名,可以使用它们分别处理处理元素插入和删除时过渡效果。其中三个类用于处理元素插入时状态A到状态B过渡,另外三个类用于处理元素删除时状态A到状态B的过渡。

在插入或显示组件时发生enter过渡,对应的类名是v-enterv-enter-activev-enter-to;在隐藏或删除组件时发生leave过渡,对应的类名是v-leavev-leave-activev-leave-to。每个类对应的作用如下:

  • v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
  • v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
  • v-enter-to: 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。
  • v-leave: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
  • v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
  • v-leave-to: 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。

用下图来阐述将会更清晰一些:

Vue 2.0学习笔记:Vue的transition

对于这些在过渡中切换的类名来说,如果你使用一个没有使用name<transition>,则 v- 是这些类名的默认前缀。如果你使用了 <transition name="fade">,那么 v-enter 会替换为 fade-enter,其他类名也是如此。另外,v-enter-activev-leave-active 可以控制进入/离开过渡的不同的缓和曲线,有关于这部分将在后面会介绍。

结合起来,我们可以用一张图来表示Vue中过渡动效的生命周期,即动效开始、过程、结束对应的类的变化:

Vue 2.0学习笔记:Vue的transition

回到上面的示例中来,在元素h1加载到DOM之前,h1会添加fade-enter类名,对应的动效也就被添加到该元素中,只对应一帧。因此,在DOM渲染时,将动画的opacity:0应用到h1元素上,此时该元素也就隐藏不可见。在动效的整个过程中,fade-enter-active类也被添加到该元素中,在该示例中,动画被设置为.5sfade-leavefade-leave-active也以相同的方式运用于h1元素上。

在示例中,并没有显式的在fade-leave类中设置opacity: 1,那是因为opacity的默认值已经是1。这也就是为什么在fade-enter-active结束时opacity也不显式设置为1的原因。

在Vue中,如果元素放置到<transition>中,Vue会自动检测元素上的v-if指令。比如上面的示例,如果isShow的值为true,那么h1会渲染显示,当你点击按钮时,会自动切换isShow的值。从而也控制了h1元素的显示或隐藏(插入或删除)。

这里需要注意的一点是,Vue只能对<transition>中的一个元素进行动效处理。相反,在任何给定的实例中,只能将<transition>中的一个元素插入到DOM中。比如下同这个示例,在Vue中无法正常的工作:

<!-- ToggleAlert.vue -->
<template>
    <div class="toogle-alert">
        <button @click="isToggle" :class="isShow ? 'is-show' : 'is-hidden'">
        {{ isShow ? "隐藏" : "显示" }}
        </button>

        <transition name="fade">
            <div class="alert alert-info" v-if="isShow" key="info">{{ alertInfoMsg }}</div>
            <div class="alert alert-error" v-if="isShow" key="error">{{ alertErrorMsg }}</div>
        </transition>
    </div>
</template>

<script>
export default {
    name: "ToggleAlert",
    data() {
        return {
            isShow: true,
            alertInfoMsg: "Hello! W3cplus.com!",
            alertErrorMsg: "Goodbye! W3cplus.com!"
        };
    },
    methods: {
        isToggle() {
            this.isShow = !this.isShow;
        }
    }
};
</script>

<style scoped>
.fade-enter,
.fade-leave-to {
    opacity: 0;
}
.fade-enter-active,
.fade-leave-activ {
    transition: opacity 0.5s cubic-bezier(1, 0.5, 0.8, 1);
}
</style>

这个时候浏览器会报错:

Vue 2.0学习笔记:Vue的transition

如果我们把另一个divv-ifv-else来替代:

在Vue中<transition>中有多个元素需要有动效效果时,需要使用<transition-group>来替代,不然在Vue中则会报错。有关于<transition-group>更详细的介绍,我们后续会单独花时间来阐述。另外,在<transition>中有相同标签名的元素切换时,需要通过 key 特性设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。即使在技术上没有必要,给在 <transition> 组件中的多个元素设置 key 是一个更好的实践。

现在,你看到的效果可以没有你想像的那么顺滑。这主要是因为,Vue在将第二个元素插入到DOM之前,并不会等第一个元素从DOM中完全删除。所以你看到效果就如上例所示,两个元素之间动效的切换似乎有点闪跳一样。不过我们可以通过transition的过渡模式来处理。

过渡模式

上面的示例存在一个问题,先看下图这个效果:

Vue 2.0学习笔记:Vue的transition

div.alert-infodiv.alert-error两个元素都被重绘了,一个离开过渡的时候另一个开始进入过渡。其实这也是<transition>的默认行为,即进入和离开同时发生

在Vue中提供了两种过渡模式来解决上述问题:

  • in-out:新元素先进行过渡,完成之后当前元素过渡离开
  • out-in:当前元素先进行过渡,完成之后新元素过渡进入

这样我们就可以使用out-in重写上面的示例:

<transition name="fade" mode="out-in">
    <div class="alert alert-info" v-if="isShow" key="info">
        {{ alertInfoMsg }}
    </div>
    <div class="alert alert-error" v-else="!isShow" key="error">
        {{ alertErrorMsg }}
    </div>
</transition>

效果如下:

设置初始渲染的过渡

在Vue中,可以通过appear特性设置节点在初始渲染的过渡。

<transition name="fade" mode="out-in" appear>
    <div class="alert alert-info" v-if="isShow" key="info">
        {{ alertInfoMsg }}
    </div>
    <div class="alert alert-error" v-else="!isShow" key="error">
        {{ alertErrorMsg }}
    </div>
</transition>

这里默认和进入和离开过渡一样,同样也可以自定义 CSS 类名:

<transition
appear
appear-class="custom-appear-class"
appear-to-class="custom-appear-to-class" 
appear-active-class="custom-appear-active-class"
>
    <!-- ... -->
</transition>

同样可以对应的类名添加样式:

.custom-appear-class {
    opacity: 0;
    transform: translateX(100%);
}
.custom-appear-active-class {
    transition: 2s;
}

比如上面的示例,按照上面的代码进行调整之后,可以发看到第一个.alert插入到DOM中时,整个.alert会从右侧移入进来,效果如下:

示例: 使用transition制作一个圆形菜单

通过上面的学习,我们接下来使用Vue的transition来实现下面这样的一个圆形菜单效果:

Vue 2.0学习笔记:Vue的transition

这个案例其实非常的简单,通过一个<transition>来触发多个子元素的过渡效果,我们只需要定义元素对应的过渡效果即可,而其他的事情都将交给Vue来搞定。

<transition name="move" mode="out-in" appear>
    <ul class="menu" v-show="isShow">
        <li
        v-for="(item, index) in menus"
        :key="index"
        class="menuitem-wrapper"
        >
            <div class="icon-holder">
                <a href="#" class="menu-item">
                    <i class="material-icons">{{ item }}</i>
                </a>
            </div>
        </li>
    </ul>
</transition>

只需要在对应的transition类中控制transform的样式:

.move-enter-active,
.move-leave-active {
    transition: all 0.08s ease-in-out;
}

.move-enter,
.move-leave-to {
    transform: scale(0);
}

最终效果如下:

总结

这篇文章主要介绍了Vue中如何使用<transition>来实现transition动效。这只是Vue中制作动效的最基础部分,但这些基础部分是帮助我们实现更为复杂的动效的基础。在接下来中我们将再一起探讨如何在Vue中实现CSS的animation效果,以入如何和第三方库一起结合实现动效。如果感兴趣的话,欢迎持续关注后续的相关更新。

原文链接:https://www.w3cplus.com/vue/vue-transition.html

发表评论

登录后才能评论