vue中的transition 标签

1,010 阅读12分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情

一、介绍

我们在使用vue开发的过程中使用v-if或者使用v-show指令的时候,元素的展示和隐藏都是十分生硬的消失或者展示,没有美感。为了使这个过程看起来缓和、美观一点,我们一般给它加个动画过渡一下。今天就介绍vue提供的组件来实现这种效果,那就是transition标签,它可以监听到元素的展示和消失,并且会自动帮我们激活样式。

二、基本使用

<template>
  <div>
    <button v-on:click="show = !show">展示</button>
    //注意这里还没有name属性
    <transition>
      <p v-if="show">hello</p>
    </transition>
  </div>
</template>

<script>
export default {
  name: "Test",
  data(){
      return{
          show: true
      }
  }
};
</script>

<style>
.v-enter-active, .v-leave-active {
  transition: opacity .5s;
}
.v-enter, .v-leave-to {
  opacity: 0;
}
</style>
</template>

效果1.gif

那到底是怎么实现的呢,其实就是当v-if的值为真时帮我们激活了v-enter和v-enter-active这两个样式类并作用在p标签上,然后当元素插入后取消这两个样式类。当v-if的值为假时激活了v-leave-to和.v-leave-active这两个样式类并作用在p标签上,当元素消失后取消这两个样式类。 那先后顺序是什么呢,别急,我们先看看vue给我们提供了哪些样式类:

在进入/离开的过渡中,会有 6 个 class 切换。

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

什么意思呢,就是有两套样式(进入和离开)

进入的样式:

元素插入是有一个过程的

v-enter 进入时过渡的开始状态,元素插入后的下一帧取消,此时元素已经存在,将要展示到页面上。

v-enter-active 元素插入的过程中激活的样式,在v-enter之前生效,在过渡/动画完成之后移除。

v-enter-to 进入时过渡的结束状态,在过渡/动画完成之后移除。此时元素已经存在,已经展示到页面上。

怎么证明呢

我们可以把样式这么写

.v-enter-active{
  transition: all 2s;
}
.v-enter{
  opacity: 0;
  font-size: 12px;
}
.v-enter-to{
   font-size: 50px;
}

就会得到这种效果

效果2.gif

一开始点击展示后v-enter-active激活,给所有的属性加上2s的过渡效果,之后v-enter激活设置了opacity和font-size属性,之后元素完成插入后v-enter-to激活,将font-size设置为50px,元素会从v-enter的样式变成v-enter-to的样式效果。由于v-enter-active的transition: all 2s;属性影响,元素会有一个从opacity:0 -> opacity:1(默认就是1) ;font-size: 12px; -> font-size: 50px;的过渡效果 ,之后过渡效果结束后v-enter-active和v-enter-to消失,元素的样式恢复默认值,所以元素瞬间变小

一句话概括:元素的插入是有一个从无到有的过程的,这个过程的起始点的样式由v-enter控制,终点的样式由v-enter-to控制,整个过程v-enter-active都参与其中。

这个过程是极其快的,如果不使用过渡或者动画,你根本看不出来有什么变化。

这里有个注意点,就是v-enter或者v-enter-to可以一起出现或者其中一个出现才会有效果,如果只有一个的话还会将另一个的样式设置成默认值,如果两个都没有,那就只有v-enter-active的样式了。

离开的样式:

元素的离开(删除或者隐藏)同样也有一个过程

v-leave 离开时过渡的开始状态,元素将要离开,下一帧开始进入离开的过程中,同时v-leave也会取消

v-leave-active 元素离开的过程中激活的样式,在v-leave之前激活,在过渡/动画完成之后移除。

v-leave-to 离开时过渡的结束状态,在过渡/动画完成之后移除。在v-leave-active之前取消,下一帧元素消失

离开的样式的细节可以参考进入样式细节。 只不过v-leave设置的样式好像没啥用。

而且transition标签只能存在一个根节点

三、transition标签的属性使用

3.1 name属性

我们知道,一个项目中不可能只有一个标签使用动画,那么多个元素想使用过渡该怎么办。比如说这样

<div>
    <button v-on:click="show1 = !show1">展示第一个元素</button>
    <button v-on:click="show2 = !show2">展示第二个元素</button>
    <transition>
      <p v-if="show1">第一个元素</p>
    </transition>
    <transition>
      <p v-if="show2">第二个元素</p>
    </transition>
</div>

同样使用上一节的样式,会得到这样一种效果

ezgif-3-35227590ec.gif

很明显,两个元素的效果是一样的,那怎么使得它们不一样就得借助vue给我们提供的transition的name属性了

  <div>
    <button v-on:click="show1 = !show1">展示第一个元素</button>
    <button v-on:click="show2 = !show2">展示第二个元素</button>
    <transition name="e1">
      <p v-if="show1">第一个元素</p>
    </transition>
    <transition name="e2">
      <p v-if="show2">第二个元素</p>
    </transition>
  </div>

加入name属性后我们将样式的v-enter、v-leave-active、v-enter-to前面的v-改成transition身上的name的值加-, 如:

.e1-enter-active,.e2-enter-active{
  transition: all 2s;
}
.e1-enter{
  opacity: 0;
  font-size: 12px;
}
.e1-enter-to{
   font-size: 50px;
   color: red;
}

.e2-enter{
  opacity: 0;
  font-size: 12px;
}
.e2-enter-to{
   font-size: 50px;
   color: skyblue;
}

ezgif-4-dfe59a39a8.gif

也就是说只要使用name属性就可以得到多个动画效果。

3.2 自定义过渡的类名

我们也可以使用vue提供的过渡类来添加过渡效果,通过这种功能使得我们不需要自己制作动画过渡效果,可以使用第三方的动画库,比如Animate.css

自定义类有六个,其效果和基本使用这一节里的v-enter、v-enter-active相似,只不过以下六个是属性名

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

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

在介绍之前,我们先引入animate.css这个动画库

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"/>

我们transition标签加上leave-active-class、enter-active-class离开时激活的类和进入时激活的类加上animate.css的效果类

  <div>
    <button v-on:click="show1 = !show1">展示/隐藏</button>
    <transition name="e1" leave-active-class="animate__animated animate__lightSpeedOutLeft" enter-active-class="animate__animated animate__flash">
      <p v-if="show1">元素</p>
    </transition>
  </div>

ezgif-4-10207d17b2.gif

非常好用

3.3 duration属性

duration属性可以使得v-enter、v-enter-active、v-enter-to、v-leave、v-leave-active、v-leave-to类的效果延迟消失或者提前消失。

  <div>
    <button v-on:click="show1 = !show1">展示第一个元素</button>
    <transition  name="e1">
      <p v-if="show1">第一个元素</p>
    </transition>
  </div>

.e1-enter-active{
  animation: anima 2s ease-in-out;
  font-size: 20px;
}
.e1-enter-to{
  color: aqua;
}
@keyframes anima {
  0% {
    transform: translateX(100px);
  }
  25%{
    transform: translateX(0px);
  }
  50%{
    transform: translateX(20px);
  }
  75%{
    transform: translateX(5px);
  }
  100% {
    transform: translateX(0px);

  }
}

未加之前的效果,这时候的动画效果是两秒完成的

ezgif-3-35434e6d94.gif

将duration属性设置为1000毫秒之后

  <div>
    <button v-on:click="show1 = !show1">展示第一个元素</button>
    <transition :duration="1000" name="e1">
      <p v-if="show1">第一个元素</p>
    </transition>
  </div>

ezgif-3-75c48eede4.gif

可以看得出来,隐藏元素需要等待一秒后才会隐藏,展示元素的动画效果在一秒后直接消失,根本没有执行完成。所以duration属性不只影响v-enter、v-enter-active、v-enter-to、v-leave、v-leave-active、v-leave-to,也可以影响元素隐藏和展示时的时间。 将duration属性设置成5000毫秒,元素会等待五秒才会消失。

ezgif-3-03a5c14a5f.gif

3.4 appear属性

给transition标签添加了appear属性且值为true后,会使得元素初始化展示时有已设置过渡或者动画效果,且只执行一次。

和appear属性类型的还有

  1. appear-class 其值表示初始展示开始时给元素添加的样式类名,效果和v-enter类似。
  2. appear-to-class 效果和v-enter-to类似。
  3. appear-active-class 效果和v-enter-active类似。

3.5 mode属性(多元素过渡)

mode属性是给transition标签添加新旧元素交替时的过渡模式,避免交替时的过渡效果发生冲突。有两个值

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

那什么是新旧元素的,新元素是指在transition标签里新增加或者新展示DOM,旧元素是指在transition标签里删除或者隐藏示DOM。

    <button v-on:click="show1 = !show1">展示</button>
    <transition name="e1">
      <p v-if="show1">第一个元素</p>
      <p v-else>第二个元素</p>
    </transition>
  data(){
      return{
          show1: true,
     }
  },

在上面这段代码中 第一个元素p标签 一加载就放入transition中,它就是此时的新元素, 当点击一次按钮后,由于v-if="show1"的结果为假,它将被移除,此时它变成了旧元素,第二个元素p标签变成了新元素,如果再点击一次,结果相反。

不过要注意

当有相同标签名的元素切换时,需要通过 key attribute 设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。即使在技术上没有必要,给在 <transition> 组件中的多个元素设置 key 是一个更好的实践。

所以要将以上代码改写变成:

 <div>
    <button v-on:click="show1 = !show1">展示</button>
    <transition name="e1">
      <p v-if="show1" key="front">第一个元素</p>
      <p v-else key="opposite">第二个元素</p>
    </transition>
  </div>

好了,了解了新旧元素的概念后我们来看一下mode属性的in-out、out-in值有什么应用和不同。

  <div  class="move">
    <button v-on:click="show1 = !show1">展示</button>
    <transition name="e1" >
      <p v-if="show1" key="front">第一个元素</p>
      <p v-else key="opposite">第二个元素</p>
    </transition>
  </div>
.move{
  width: 50vw;
  margin: 0 auto;
}
.e1-enter-active{
  transition: all 2s;
  font-size: 20px;
}
.e1-enter-to{
  color: aqua;
  transform: translateX(100px);
}
.e1-leave-active{
  transition: all 2s;
  font-size: 20px;
}
.e1-leave-to{
  color: aqua;
  transform: translateX(100px);
}

3.5.1 未使用mode属性

我们给transition里的元素添加了进入时和离开时的过渡,先看一下没有加mode属性时的离开/进入效果。

ezgif-4-aa07d9c577.gif

可以看得出来,在不加入mode属性时进入和消失的过渡是同时进行的。

3.5.1 out-in

ezgif-3-263496e0bb.gif

加入mode="out-in"后元素离开的过渡先执行,进入的过渡后执行。

3.5.1 in-out

ezgif-3-74fa533d7d.gif

加入mode="in-out"后元素进入的过渡先执行,离开的过渡后执行。

四、transition-group 列表过渡

很多时候我们不可能只有一两个节点,在使用v-for渲染列表的时候会存在很多个节点,但是我们又想使得这些节点在新增很删除的时候有一个过渡效果,由于transition只能存在一个根节点的原因显然满足不了。

因此vue给我们提供了transition-group标签来帮助我们完成。

和transition标签相比,transition-group标签有以下特点

  1. ransition-group标签是一个真实的dom,默认为一个 <span>标签,如果想改变默认值,可以给ransition-group标签添加tag属性并附上标签值来更换为其他元素。例如<transition-group tag="div">
  2. ransition-group标签 除了mode属性不可用之外,其他属性和transition标签一样
  3. 在使用 ransition-group标签后,里面的元素必须得携带不一样的key值
  4. ransition-group标签的过渡类不是作用在其本身上,而是给每一个亲子元素都添加相同的过渡类

4.1基本使用

<template>
  <div>
      <button @click="ishow = !ishow">显示/隐藏元素</button>
      <transition-group  :appear="true"  name="test">
        <template  v-for="(item,index)  in 5" >
            <p class="test" v-show="(ishow+index)%2===0" :key="index">元素{{index}}</p>
        </template>
      </transition-group>
  </div>
</template>
        data(){
            return{
                ishow:false,
            }
        }
 .test{
        background-color: bisque;
        height: 100px;
    }
    .test-enter,
    .test-leave-to{
        transform: translateX(-100%);
    }
    .test-enter-active,
    .test-leave-active{
        transition: all .5s linear
    }
    .test-enter-to,
    .test-leave{
        transform: translateX(0px);
    }

使用transition-group后,我们给v-for里面的每一个元素都添加了一个进入和离开的过渡

ezgif-5-5e8c001136.gif

4.2 v-move的作用

v-move是transition-group里类似于v-enter的一种添加类的方式,当元素在父元素里的排序发生变化时给全部排序发生变化的元素添加该类(.v-move),并且使用 transforms 将元素从之前的位置平滑过渡新的位置。前提是你得在.v-move加入transition属性且 transition-property 的值是transform或者all才能看得效果。

<template>
  <div>
      <button @click="ishow = !ishow">显示/隐藏元素</button>
      <button @click="add">添加一个元素</button>
      <transition-group  :appear="true"  name="test" class="testBox">
        <template  v-for="(item)  in numArr" >
            <p class="test" v-show="ishow" :key="item">{{item}}</p>
        </template>
      </transition-group>
  </div>
</template>
data(){
    return{
        ishow:false,
        numArr:[1,2]
    }
},
methods:{
   add(){
       let index = Math.floor(Math.random() * this.numArr.length)
       this.numArr.splice(index,0,this.numArr.length+1)
    }
}
    .testBox{
        display: flex;
        align-content: center;
    }
    .test{
      font-size: 28px;
      margin-left: 50px;
    }
    .test-enter,
    .test-leave-to{
        opacity: 0;
        transform: translateY(100%);
    }
    .test-enter-active,
    .test-leave-active{
        transition: all .5s linear
    }
    .test-enter-to,
    .test-leave{
        opacity: 1;
        transform: translateY(0px);
    }
    .test-move{
        transition: all  1s linear;     
    }

ezgif-2-2279a97585.gif

五、总结

本文通过简单的案例介绍了vue里的transition和transition-group标签,基本介绍了transition和transition-group标签的基本使用和其属性的介绍,在一定程度上参考了vue官网对transition和transition-group标签的介绍。因水平有限,在某些理解上可能存在错误和误差,欢迎各位指出。开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情