Vue <Transition> 过渡组件


示例

使用内置的 <Transition> 组件为 <p> 元素设置动画,因为它被 v-if 删除:

<Transition>
  <p v-if="exists">Hello World!</p>
</Transition>
运行示例 »

请参阅下面的更多示例。


定义和用法

内置的 <Transition> 组件用于在使用 v-if, v-show 或动态组件添加或删除元素时对元素进行动画处理。

元素动画的规则是在自动生成的类或 JavaScript 过渡钩子中编写的。 请参阅下表。

<Transition> 组件的根级别只能有一个元素。


Props 选项

Props 选项 描述
none 默认。 运行示例 »
appear 如果设置为true,则该元素在第一次安装时也会有动画效果。 默认值为 false 运行示例 »
mode mode="out-in" 确保初始元素在下一个元素进入之前离开。mode="in-out" 确保新元素在旧元素离开之前进入。 默认情况下,旧元素在新元素进入的同时离开。 运行示例 »
name 指定过渡的名称。 如果我们有多个过渡,我们需要给它们起唯一的名称来区分它们。name="swirl" 确保 CSS 过渡类以 swirl- 开头,而不是默认前缀 v- 运行示例 »
css 布尔值。:css="false" 告诉 Vue 编译器此过渡不使用过渡类,仅使用 JavaScript 钩子。 设置此属性后,必须在 Enter 和 Leave 钩子内部使用 done() 回调。 运行示例 »
type 指定是否等待"animation"或"transition"完成过渡。 如果同时设置了 CSS 动画和 CSS 过渡,并且未设置此 type 属性,Vue 将检测这两者的最长持续时间并将其用作过渡时间。
duration 指定"进入"和"离开"的过渡时间长度。 默认是在 CSS 动画或 CSS 过渡结束时结束。 具体时间可以这样定义::duration="{enter:2000, leave:1000 }",或者duration="1000"
enterFromClass
enterActiveClass
enterToClass
appearFromClass
appearActiveClass
appearToClass
leaveFromClass
leaveActiveClass
leaveToClass
使用这些属性来重命名过渡类。

使用像 enter-active-class="entering" 这样的属性之一意味着这个过渡类可以在 CSS 中称为 .entering,而不是默认的 .v-enter-active。 约定是在模板中使用 kebab-case 来表示属性,以符合 HTML 中属性的编写方式。

运行示例 »

CSS 过渡类

当我们使用 b1 组件时,我们会自动获得六个不同的 CSS 类,我们可以使用它们在添加或删除元素时对元素进行动画处理。

这些类在元素添加(进入)或删除(离开)的不同阶段处于活动状态:

过渡类 描述
v-enter-from 进入阶段开始时元素的初始样式 运行示例 »
v-enter-active 进入阶段元素的样式 运行示例 »
v-enter-to 进入阶段结束时元素的样式 运行示例 »
v-leave-from 离开阶段开始时元素的初始样式 运行示例 »
v-leave-active 离开阶段元素的样式 运行示例 »
v-leave-to 离开阶段结束时元素的样式 运行示例 »

JavaScript 过渡钩子

上面的过渡类对应于我们可以钩子以运行 JavaScript 代码的事件。

JavaScript 事件 描述
before-enter 在进入阶段开始时调用
enter 在输入阶段的"before-enter"钩子之后调用 运行示例 »
after-enter 在输入转换结束时调用 运行示例 »
enter-cancelled 如果取消输入过渡则调用 运行示例 »
before-leave 在离开阶段开始时调用 运行示例 »
leave 在离开阶段的"before-leave"钩子之后调用 运行示例 »
after-leave 在离开过渡结束时调用
leave-cancelled 只有在使用v-show并且取消离开阶段时才会调用此方法

更多示例

示例 1

<p> 元素在切换时会滑入和滑出。

<template>
  <h1>Add/Remove <p> Tag</h1>
  <button @click="this.exists = !this.exists">{{btnText}}</button><br>
  <Transition>
    <p v-if="exists">Hello World!</p>
  </Transition>
</template>

<script>
export default {
  data() {
    return {
      exists: false
    }
  },
  computed: {
    btnText() {
      if(this.exists) {
        return 'Remove';
      }
      else {
        return 'Add';
      }
    }
  }
}
</script>

<style>
  .v-enter-from {
    opacity: 0;
    translate: -100px 0;
  }
  .v-enter-to {
    opacity: 1;
    translate: 0 0;
  }
  .v-leave-from {
    opacity: 1;
    translate: 0 0;
  }
  .v-leave-to {
    opacity: 0;
    translate: 100px 0;
  }
  p {
    background-color: lightgreen;
    display: inline-block;
    padding: 10px;
    transition: all 0.5s;
  }
</style>
运行示例 »

示例 2

<p> 元素在"进入"和"离开"期间具有不同的背景颜色

<template>
  <h1>Add/Remove <p> Tag</h1>
  <button @click="this.exists = !this.exists">{{btnText}}</button><br>
  <Transition>
    <p v-if="exists">Hello World!</p>
  </Transition>
</template>

<script>
export default {
  data() {
    return {
      exists: false
    }
  },
  computed: {
    btnText() {
      if(this.exists) {
        return 'Remove';
      }
      else {
        return 'Add';
      }
    }
  }
}
</script>

<style>
  .v-enter-active {
    background-color: lightgreen;
    animation: added 1s;
  }
  .v-leave-active {
    background-color: lightcoral;
    animation: added 1s reverse;
  }
  @keyframes added {
    from {
      opacity: 0;
      translate: -100px 0;
    }
    to {
      opacity: 1;
      translate: 0 0;
    }
  }
  p {
    display: inline-block;
    padding: 10px;
    border: dashed black 1px;
  }
</style>
运行示例 »

示例 3

<p> 元素的动画效果不同,使用 name 属性来区分 <Transition> 组件。

<template>
  <h1>Add/Remove <p> Tag</h1>
  <p>本例中的第二个转换的名称为 prop "swirl",这样我们就可以使用不同的类名来将转换分开。</p>
  <hr>
  <button @click="this.p1Exists = !this.p1Exists">{{btn1Text}}</button><br>
  <Transition>
    <p v-if="p1Exists" id="p1">Hello World!</p>
  </Transition>
  <hr>
  <button @click="this.p2Exists = !this.p2Exists">{{btn2Text}}</button><br>
  <Transition name="swirl">
    <p v-if="p2Exists" id="p2">Hello World!</p>
  </Transition>
</template>

<script>
export default {
  data() {
    return {
      p1Exists: false,
      p2Exists: false
    }
  },
  computed: {
    btn1Text() {
      if(this.p1Exists) {
        return 'Remove';
      }
      else {
        return 'Add';
      }
    },
    btn2Text() {
      if(this.p2Exists) {
        return 'Remove';
      }
      else {
        return 'Add';
      }
    }
  }
}
</script>

<style>
  .v-enter-active {
    background-color: lightgreen;
    animation: added 1s;
  }
  .v-leave-active {
    background-color: lightcoral;
    animation: added 1s reverse;
  }
  @keyframes added {
    from {
      opacity: 0;
      translate: -100px 0;
    }
    to {
      opacity: 1;
      translate: 0 0;
    }
  }
  .swirl-enter-active {
    animation: swirlAdded 1s;
  }
  .swirl-leave-active {
    animation: swirlAdded 1s reverse;
  }
  @keyframes swirlAdded {
    from {
      opacity: 0;
      rotate: 0;
      scale: 0.1;
    }
    to {
      opacity: 1;
      rotate: 360deg;
      scale: 1;
    }
  }
  #p1, #p2 {
    display: inline-block;
    padding: 10px;
    border: dashed black 1px;
  }
  #p2 {
    background-color: lightcoral;
  }
</style>
运行示例 »

示例 4

after-enter 事件触发显示 <div> 元素。

<template>
  <h1>JavaScript 过渡钩子</h1>
  <p>此代码挂接到 "after-enter" 中,以便在初始动画完成后,运行一个方法来显示红色 div。</p>
  <button @click="pVisible=true">Create p-tag!</button><br>
  <Transition @after-enter="onAfterEnter">
    <p v-show="pVisible" id="p1">Hello World!</p>
  </Transition>
  <br>
  <div v-show="divVisible">This appears after the "enter-active" phase of the transition.</div>
</template>

<script>
export default {
  data() {
    return {
      pVisible: false,
      divVisible: false
    }
  },
  methods: {
    onAfterEnter() {
      this.divVisible = true;
    }
  }
}
</script>

<style>
  .v-enter-active {
    animation: swirlAdded 1s;
  }
  @keyframes swirlAdded {
    from {
      opacity: 0;
      rotate: 0;
      scale: 0.1;
    }
    to {
      opacity: 1;
      rotate: 360deg;
      scale: 1;
    }
  }
  #p1, div {
    display: inline-block;
    padding: 10px;
    border: dashed black 1px;
  }
  #p1 {
    background-color: lightgreen;
  }
  div {
    background-color: lightcoral;
  }
</style>
运行示例 »

示例 5

切换按钮会触发 enter-cancelled 事件。

<template>
  <h1>'enter-cancelled' 事件</h1>
  <p>在输入动画完成之前再次单击切换按钮会触发'enter-cancelled'事件。</p>
  <button @click="pVisible=!pVisible">Toggle</button><br>
  <Transition @enter-cancelled="onEnterCancelled">
    <p v-if="pVisible" id="p1">Hello World!</p>
  </Transition>
  <br>
  <div v-if="divVisible">You interrupted the "enter-active" transition.</div>
</template>

<script>
export default {
  data() {
    return {
      pVisible: false,
      divVisible: false
    }
  },
  methods: {
    onEnterCancelled() {
      this.divVisible = true;
    }
  }
}
</script>

<style>
  .v-enter-active {
    animation: swirlAdded 2s;
  }
  @keyframes swirlAdded {
    from {
      opacity: 0;
      rotate: 0;
      scale: 0.1;
    }
    to {
      opacity: 1;
      rotate: 720deg;
      scale: 1;
    }
  }
  #p1, div {
    display: inline-block;
    padding: 10px;
    border: dashed black 1px;
  }
  #p1 {
    background-color: lightgreen;
  }
  div {
    background-color: lightcoral;
  }
</style>
运行示例 »

示例 6

appear 属性在页面加载后立即启动 <p> 元素动画。

<template>
  <h1>The 'appear' Prop</h1>
  <p>当页面打开时第一次呈现下面的 p 标签时,"appear"属性会启动动画。 如果没有 'appear' 属性,这个例子就没有动画。</p>
  <Transition appear>
    <p id="p1">Hello World!</p>
  </Transition>
</template>

<style>
  .v-enter-active {
    animation: swirlAdded 1s;
  }
  @keyframes swirlAdded {
    from {
      opacity: 0;
      rotate: 0;
      scale: 0.1;
    }
    to {
      opacity: 1;
      rotate: 360deg;
      scale: 1;
    }
  }
  #p1 {
    display: inline-block;
    padding: 10px;
    border: dashed black 1px;
    background-color: lightgreen;
  }
</style>
运行示例 »

示例 7

通过"进入"和"离开"动画翻阅图像。 在删除旧图像之前会添加新图像。

<template>
  <h1>元素之间的过渡</h1>
  <p>单击按钮获取新图像。</p>
  <p>新图像会在旧图像被删除之前添加。 我们将在下一个示例中使用 mode="out-in" 修复此问题。</p>
  <button @click="newImg">Next image</button><br>
  <Transition>
    <img src="/img_pizza.svg" v-if="imgActive === 'pizza'">
    <img src="/img_apple.svg" v-else-if="imgActive === 'apple'">
    <img src="/img_cake.svg" v-else-if="imgActive === 'cake'">
    <img src="/img_fish.svg" v-else-if="imgActive === 'fish'">
    <img src="/img_rice.svg" v-else-if="imgActive === 'rice'">
  </Transition>
</template>

<script>
export default {
  data() {
    return {
      imgActive: 'pizza',
      imgs: ['pizza', 'apple', 'cake', 'fish', 'rice'],
      indexNbr: 0
    }
  },
  methods: {
    newImg() {
      this.indexNbr++;
      if(this.indexNbr >= this.imgs.length) {
        this.indexNbr = 0;
      }
      this.imgActive = this.imgs[this.indexNbr];
    }
  }
}
</script>

<style>
  .v-enter-active {
    animation: swirlAdded 1s;
  }
  .v-leave-active {
    animation: swirlAdded 1s reverse;
  }
  @keyframes swirlAdded {
    from {
      opacity: 0;
      rotate: 0;
      scale: 0.1;
    }
    to {
      opacity: 1;
      rotate: 360deg;
      scale: 1;
    }
  }
  img {
    width: 100px;
    margin: 20px;
  }
  img:hover {
    cursor: pointer;
  }
</style>
运行示例 »

示例 8

通过"进入"和"离开"动画翻阅图像。 mode="out-in" 会阻止添加新图像,直到旧图像被删除为止。

<template>
  <h1>mode="out-in"</h1>
  <p>单击按钮获取新图像。</p>
  <p>使用 mode="out-in" 时,在删除当前图像之前不会添加下一个图像。 与前面示例的另一个区别是,这里我们使用计算属性而不是方法。</p>
  <button @click="indexNbr++">Next image</button><br>
  <Transition mode="out-in">
    <img src="/img_pizza.svg" v-if="imgActive === 'pizza'">
    <img src="/img_apple.svg" v-else-if="imgActive === 'apple'">
    <img src="/img_cake.svg" v-else-if="imgActive === 'cake'">
    <img src="/img_fish.svg" v-else-if="imgActive === 'fish'">
    <img src="/img_rice.svg" v-else-if="imgActive === 'rice'">
  </Transition>
</template>

<script>
export default {
  data() {
    return {
      imgs: ['pizza', 'apple', 'cake', 'fish', 'rice'],
      indexNbr: 0
    }
  },
  computed: {
    imgActive() {
      if(this.indexNbr >= this.imgs.length) {
        this.indexNbr = 0;
      }
      return this.imgs[this.indexNbr];
    }
  }
}
</script>

<style>
  .v-enter-active {
    animation: swirlAdded 0.7s;
  }
  .v-leave-active {
    animation: swirlAdded 0.7s reverse;
  }
  @keyframes swirlAdded {
    from {
      opacity: 0;
      rotate: 0;
      scale: 0.1;
    }
    to {
      opacity: 1;
      rotate: 360deg;
      scale: 1;
    }
  }
  img {
    width: 100px;
    margin: 20px;
  }
  img:hover {
    cursor: pointer;
  }
</style>
运行示例 »

示例 9

组件之间的切换是动画的。

<template>
  <h1>使用动态组件进行过渡</h1>
  <p>Transition 组件包裹着动态组件,以便可以以动画方式进行切换。</p>
  <button @click="toggleValue = !toggleValue">Switch component</button>
  <Transition mode="out-in">
    <component :is="activeComp"></component>
  </Transition>
</template>

<script>
  export default {
    data () {
      return {
        toggleValue: true
      }
    },
    computed: {
      activeComp() {
        if(this.toggleValue) {
          return 'comp-one'
        }
        else {
          return 'comp-two'
        }
      }
    }
  }
</script>

<style>
  .v-enter-active {
    animation: slideIn 0.5s;
  }
  @keyframes slideIn {
    from {
      translate: -200px 0;
      opacity: 0;
    }
    to {
      translate: 0 0;
      opacity: 1;
    }
  }
  .v-leave-active {
    animation: slideOut 0.5s;
  }
  @keyframes slideOut {
    from {
      translate: 0 0;
      opacity: 1;
    }
    to {
      translate: 200px 0;
      opacity: 0;
    }
  }
  #app {
    width: 350px;
    margin: 10px;
  }
  #app > div {
    border: solid black 2px;
    padding: 10px;
    margin-top: 10px;
  }
</style>
运行示例 »

示例 10

组件之间的切换采用动画方式。

<template>
  <h1>The :css="false" Prop</h1>
  <p>将 'css' 属性设置为 'false' 后,我们告诉编译器使用 JavaScript 钩子而不是 CSS 过渡类。</p>
  <p>当我们使用 :css="false" 时,我们必须在 'enter' 和 'leave' 钩子中调用 done() ,以告诉浏览器这些过渡何时完成。</p>
  <button @click="pVisible=!pVisible">Toggle</button>
  <div>
    <Transition
      :css="false" 
      @enter="onEnter"
      @after-enter="onAfterEnter"
      @before-leave="onBeforeLeave"
      @leave="onLeave"
    >
      <p 
        v-if="pVisible"
        id="p1">
        Hello World!
      </p>
    </Transition>
  </div>
</template>

<script>
export default {
  data() {
    return {
      pVisible: false
    }
  },
  methods: {
    onEnter(el,done) {
      let pos = 0;
      window.requestAnimationFrame(frame);
      function frame() {
        if (pos > 150) {
          done();
        } else {
          pos++; 
          el.style.left = pos + "px"; 
          window.requestAnimationFrame(frame);
        }
      }
    },
    onAfterEnter(el) {
      el.style.backgroundColor = "yellow";
    },
    onBeforeLeave(el) {
      el.style.backgroundColor = "lightgreen";
    },
    onLeave(el,done) {
      let pos = 150;
      window.requestAnimationFrame(frame);
      function frame() {
        if (pos < 0) {
          done();
        }
        else {
          pos--;
          el.style.left = pos + "px"; 
          window.requestAnimationFrame(frame);
        }
      }
    }
  }
}
</script>

<style>
  #p1 {
    position: absolute;
    padding: 10px;
    border: dashed black 1px;
    background-color: lightgreen;
  }
  #app > div {
    position: relative;
    background-color: coral;
    width: 300px;
    height: 300px;
    border: dashed black 1px;
    margin-top: 20px;
  }
</style>
运行示例 »

示例 11

使用 enterActiveClass 属性将 "v-enter-active" CSS 类重命名为"entering"。

<template>
  <h1>The 'enterActiveClass' Prop</h1>
  <button @click="this.exists = !this.exists">{{btnText}}</button><br>
  <Transition enter-active-class="entering">
    <p v-if="exists">Hello World!</p>
  </Transition>
</template>

<script>
export default {
  data() {
    return {
      exists: false
    }
  },
  computed: {
    btnText() {
      if(this.exists) {
        return 'Remove';
      }
      else {
        return 'Add';
      }
    }
  }
}
</script>

<style>
  .entering {
    background-color: lightgreen;
    animation: added 1s;
  }
  .v-leave-active {
    background-color: lightcoral;
    animation: added 1s reverse;
  }
  @keyframes added {
    from {
      opacity: 0;
      translate: -100px 0;
    }
    to {
      opacity: 1;
      translate: 0 0;
    }
  }
  p {
    display: inline-block;
    padding: 10px;
    border: dashed black 1px;
  }
</style>
运行示例 »

相关页面

Vue 教程:Vue 动画

Vue 教程:使用 v-for 的 Vue 动画

Vue 参考:Vue <TransitionGroup> 组件