Vue <Transition> 组件

实例

使用内置的 <Transition> 组件在 <p> 元素被 v-if 移除时为其设置动画:

  1. <Transition>
  2. <p v-if="exists">Hello World!</p>
  3. </Transition>

定义与用法

内置的 <Transition> 组件用于在使用 v-if, v-show 或动态组件添加或删除元素时为其设置动画。

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

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


Props

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

使用像这样的 prop,比如 enter-active-class="entering" 意味着这个过渡类在 CSS 中可以被写为 .entering,而不是默认的 .v-enter-active 惯例是对模板中的 prop 使用 kebab-case,以符合在 HTML 中编写属性的方式。


CSS Transition 过渡类

当我们使用 <Transition> 组件时,我们会自动获得 6 个不同的 CSS 类,当添加或删除元素时,我们可以使用它们来设置动画。

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

Transition 类描述
v-enter-from进入阶段开始时元素的初始样式
v-enter-active元素在进入阶段的样式
v-enter-to元素的样式位于进入阶段的末尾
v-leave-from离开阶段开始时元素的初始样式
v-leave-active元素在离开阶段的样式
v-leave-to元素的样式位于离开阶段的末尾

JavaScript Transition 钩子

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

JavaScript 事件描述
before-enter在进入阶段开始时调用
enter在 'before-enter' 钩子之后,在进入阶段调用
after-enter在进入过渡结束时调用
enter-cancelled如果进入过渡被取消,则调用
before-leave在离开阶段开始时调用
leave在 'before-leave' 钩子之后,在离开阶段调用
after-leave在离开过渡结束时立即调用
leave-cancelled只有在使用 v-show 并且取消离开阶段时才会调用此操作

更多实例

实例 1

<p> 元素被切换时,它会滑入和滑出。

  1. <template>
  2. <h1>Add/Remove <p> Tag</h1>
  3. <button @click="this.exists = !this.exists">{{btnText}}</button><br>
  4. <Transition>
  5. <p v-if="exists">Hello World!</p>
  6. </Transition>
  7. </template>
  8. <script>
  9. export default {
  10. data() {
  11. return {
  12. exists: false
  13. }
  14. },
  15. computed: {
  16. btnText() {
  17. if(this.exists) {
  18. return 'Remove';
  19. }
  20. else {
  21. return 'Add';
  22. }
  23. }
  24. }
  25. }
  26. </script>
  27. <style>
  28. .v-enter-from {
  29. opacity: 0;
  30. translate: -100px 0;
  31. }
  32. .v-enter-to {
  33. opacity: 1;
  34. translate: 0 0;
  35. }
  36. .v-leave-from {
  37. opacity: 1;
  38. translate: 0 0;
  39. }
  40. .v-leave-to {
  41. opacity: 0;
  42. translate: 100px 0;
  43. }
  44. p {
  45. background-color: lightgreen;
  46. display: inline-block;
  47. padding: 10px;
  48. transition: all 0.5s;
  49. }
  50. </style>
实例 2

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

  1. <template>
  2. <h1>Add/Remove <p> Tag</h1>
  3. <button @click="this.exists = !this.exists">{{btnText}}</button><br>
  4. <Transition>
  5. <p v-if="exists">Hello World!</p>
  6. </Transition>
  7. </template>
  8. <script>
  9. export default {
  10. data() {
  11. return {
  12. exists: false
  13. }
  14. },
  15. computed: {
  16. btnText() {
  17. if(this.exists) {
  18. return 'Remove';
  19. }
  20. else {
  21. return 'Add';
  22. }
  23. }
  24. }
  25. }
  26. </script>
  27. <style>
  28. .v-enter-active {
  29. background-color: lightgreen;
  30. animation: added 1s;
  31. }
  32. .v-leave-active {
  33. background-color: lightcoral;
  34. animation: added 1s reverse;
  35. }
  36. @keyframes added {
  37. from {
  38. opacity: 0;
  39. translate: -100px 0;
  40. }
  41. to {
  42. opacity: 1;
  43. translate: 0 0;
  44. }
  45. }
  46. p {
  47. display: inline-block;
  48. padding: 10px;
  49. border: dashed black 1px;
  50. }
  51. </style>
实例 3

<p> 元素以不同的方式设置动画,使用命名 prop 来区分<Transition>组件。

  1. <template>
  2. <h1>Add/Remove <p> Tag</h1>
  3. <p>The second transition in this example has the name prop "swirl", so that we can keep the transitions apart with different class names.</p>
  4. <hr>
  5. <button @click="this.p1Exists = !this.p1Exists">{{btn1Text}}</button><br>
  6. <Transition>
  7. <p v-if="p1Exists" id="p1">Hello World!</p>
  8. </Transition>
  9. <hr>
  10. <button @click="this.p2Exists = !this.p2Exists">{{btn2Text}}</button><br>
  11. <Transition name="swirl">
  12. <p v-if="p2Exists" id="p2">Hello World!</p>
  13. </Transition>
  14. </template>
  15. <script>
  16. export default {
  17. data() {
  18. return {
  19. p1Exists: false,
  20. p2Exists: false
  21. }
  22. },
  23. computed: {
  24. btn1Text() {
  25. if(this.p1Exists) {
  26. return 'Remove';
  27. }
  28. else {
  29. return 'Add';
  30. }
  31. },
  32. btn2Text() {
  33. if(this.p2Exists) {
  34. return 'Remove';
  35. }
  36. else {
  37. return 'Add';
  38. }
  39. }
  40. }
  41. }
  42. </script>
  43. <style>
  44. .v-enter-active {
  45. background-color: lightgreen;
  46. animation: added 1s;
  47. }
  48. .v-leave-active {
  49. background-color: lightcoral;
  50. animation: added 1s reverse;
  51. }
  52. @keyframes added {
  53. from {
  54. opacity: 0;
  55. translate: -100px 0;
  56. }
  57. to {
  58. opacity: 1;
  59. translate: 0 0;
  60. }
  61. }
  62. .swirl-enter-active {
  63. animation: swirlAdded 1s;
  64. }
  65. .swirl-leave-active {
  66. animation: swirlAdded 1s reverse;
  67. }
  68. @keyframes swirlAdded {
  69. from {
  70. opacity: 0;
  71. rotate: 0;
  72. scale: 0.1;
  73. }
  74. to {
  75. opacity: 1;
  76. rotate: 360deg;
  77. scale: 1;
  78. }
  79. }
  80. #p1, #p2 {
  81. display: inline-block;
  82. padding: 10px;
  83. border: dashed black 1px;
  84. }
  85. #p2 {
  86. background-color: lightcoral;
  87. }
  88. </style>
实例 4

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

  1. <template>
  2. <h1>JavaScript Transition Hooks</h1>
  3. <p>This code hooks into "after-enter" so that after the initial animation is done, a method runs that displays a red div.</p>
  4. <button @click="pVisible=true">Create p-tag!</button><br>
  5. <Transition @after-enter="onAfterEnter">
  6. <p v-show="pVisible" id="p1">Hello World!</p>
  7. </Transition>
  8. <br>
  9. <div v-show="divVisible">This appears after the "enter-active" phase of the transition.</div>
  10. </template>
  11. <script>
  12. export default {
  13. data() {
  14. return {
  15. pVisible: false,
  16. divVisible: false
  17. }
  18. },
  19. methods: {
  20. onAfterEnter() {
  21. this.divVisible = true;
  22. }
  23. }
  24. }
  25. </script>
  26. <style>
  27. .v-enter-active {
  28. animation: swirlAdded 1s;
  29. }
  30. @keyframes swirlAdded {
  31. from {
  32. opacity: 0;
  33. rotate: 0;
  34. scale: 0.1;
  35. }
  36. to {
  37. opacity: 1;
  38. rotate: 360deg;
  39. scale: 1;
  40. }
  41. }
  42. #p1, div {
  43. display: inline-block;
  44. padding: 10px;
  45. border: dashed black 1px;
  46. }
  47. #p1 {
  48. background-color: lightgreen;
  49. }
  50. div {
  51. background-color: lightcoral;
  52. }
  53. </style>
实例 5

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

  1. <template>
  2. <h1>The 'enter-cancelled' Event</h1>
  3. <p>Click the toggle button again before the enter animation is finished to trigger the 'enter-cancelled' event.</p>
  4. <button @click="pVisible=!pVisible">Toggle</button><br>
  5. <Transition @enter-cancelled="onEnterCancelled">
  6. <p v-if="pVisible" id="p1">Hello World!</p>
  7. </Transition>
  8. <br>
  9. <div v-if="divVisible">You interrupted the "enter-active" transition.</div>
  10. </template>
  11. <script>
  12. export default {
  13. data() {
  14. return {
  15. pVisible: false,
  16. divVisible: false
  17. }
  18. },
  19. methods: {
  20. onEnterCancelled() {
  21. this.divVisible = true;
  22. }
  23. }
  24. }
  25. </script>
  26. <style>
  27. .v-enter-active {
  28. animation: swirlAdded 2s;
  29. }
  30. @keyframes swirlAdded {
  31. from {
  32. opacity: 0;
  33. rotate: 0;
  34. scale: 0.1;
  35. }
  36. to {
  37. opacity: 1;
  38. rotate: 720deg;
  39. scale: 1;
  40. }
  41. }
  42. #p1, div {
  43. display: inline-block;
  44. padding: 10px;
  45. border: dashed black 1px;
  46. }
  47. #p1 {
  48. background-color: lightgreen;
  49. }
  50. div {
  51. background-color: lightcoral;
  52. }
  53. </style>
实例 6

页面加载后,显示 prop 立即启动 <p> 元素动画。

  1. <template>
  2. <h1>The 'appear' Prop</h1>
  3. <p>The 'appear' prop starts the animation when the p tag below is rendered for the first time as the page opens. Without the 'appear' prop, this example would have had no animation.</p>
  4. <Transition appear>
  5. <p id="p1">Hello World!</p>
  6. </Transition>
  7. </template>
  8. <style>
  9. .v-enter-active {
  10. animation: swirlAdded 1s;
  11. }
  12. @keyframes swirlAdded {
  13. from {
  14. opacity: 0;
  15. rotate: 0;
  16. scale: 0.1;
  17. }
  18. to {
  19. opacity: 1;
  20. rotate: 360deg;
  21. scale: 1;
  22. }
  23. }
  24. #p1 {
  25. display: inline-block;
  26. padding: 10px;
  27. border: dashed black 1px;
  28. background-color: lightgreen;
  29. }
  30. </style>
实例 7

在动画处于 'enter' 和 'leave' 的情况下翻转图片。在删除旧图片之前会添加新图片。

  1. <template>
  2. <h1>Transition Between Elements</h1>
  3. <p>Click the button to get a new image.</p>
  4. <p>The new image is added before the previous is removed. We will fix this in the next example with mode="out-in".</p>
  5. <button @click="newImg">Next image</button><br>
  6. <Transition>
  7. <img src="/img_pizza.svg" v-if="imgActive === 'pizza'">
  8. <img src="/img_apple.svg" v-else-if="imgActive === 'apple'">
  9. <img src="/img_cake.svg" v-else-if="imgActive === 'cake'">
  10. <img src="/img_fish.svg" v-else-if="imgActive === 'fish'">
  11. <img src="/img_rice.svg" v-else-if="imgActive === 'rice'">
  12. </Transition>
  13. </template>
  14. <script>
  15. export default {
  16. data() {
  17. return {
  18. imgActive: 'pizza',
  19. imgs: ['pizza', 'apple', 'cake', 'fish', 'rice'],
  20. indexNbr: 0
  21. }
  22. },
  23. methods: {
  24. newImg() {
  25. this.indexNbr++;
  26. if(this.indexNbr >= this.imgs.length) {
  27. this.indexNbr = 0;
  28. }
  29. this.imgActive = this.imgs[this.indexNbr];
  30. }
  31. }
  32. }
  33. </script>
  34. <style>
  35. .v-enter-active {
  36. animation: swirlAdded 1s;
  37. }
  38. .v-leave-active {
  39. animation: swirlAdded 1s reverse;
  40. }
  41. @keyframes swirlAdded {
  42. from {
  43. opacity: 0;
  44. rotate: 0;
  45. scale: 0.1;
  46. }
  47. to {
  48. opacity: 1;
  49. rotate: 360deg;
  50. scale: 1;
  51. }
  52. }
  53. img {
  54. width: 100px;
  55. margin: 20px;
  56. }
  57. img:hover {
  58. cursor: pointer;
  59. }
  60. </style>
实例 8

在动画处于 'enter' 和 'leave' 的情况下翻转图片。mode="out-in" 阻止添加新图片,直到删除旧图片为止。

  1. <template>
  2. <h1>mode="out-in"</h1>
  3. <p>Click the button to get a new image.</p>
  4. <p>With mode="out-in", the next image is not added until the current image is removed. Another difference from the previous example, is that here we use computed prop instead of a method.</p>
  5. <button @click="indexNbr++">Next image</button><br>
  6. <Transition mode="out-in">
  7. <img src="/img_pizza.svg" v-if="imgActive === 'pizza'">
  8. <img src="/img_apple.svg" v-else-if="imgActive === 'apple'">
  9. <img src="/img_cake.svg" v-else-if="imgActive === 'cake'">
  10. <img src="/img_fish.svg" v-else-if="imgActive === 'fish'">
  11. <img src="/img_rice.svg" v-else-if="imgActive === 'rice'">
  12. </Transition>
  13. </template>
  14. <script>
  15. export default {
  16. data() {
  17. return {
  18. imgs: ['pizza', 'apple', 'cake', 'fish', 'rice'],
  19. indexNbr: 0
  20. }
  21. },
  22. computed: {
  23. imgActive() {
  24. if(this.indexNbr >= this.imgs.length) {
  25. this.indexNbr = 0;
  26. }
  27. return this.imgs[this.indexNbr];
  28. }
  29. }
  30. }
  31. </script>
  32. <style>
  33. .v-enter-active {
  34. animation: swirlAdded 0.7s;
  35. }
  36. .v-leave-active {
  37. animation: swirlAdded 0.7s reverse;
  38. }
  39. @keyframes swirlAdded {
  40. from {
  41. opacity: 0;
  42. rotate: 0;
  43. scale: 0.1;
  44. }
  45. to {
  46. opacity: 1;
  47. rotate: 360deg;
  48. scale: 1;
  49. }
  50. }
  51. img {
  52. width: 100px;
  53. margin: 20px;
  54. }
  55. img:hover {
  56. cursor: pointer;
  57. }
  58. </style>
实例 9

组件之间的切换设置动画。

  1. <template>
  2. <h1>Transition with Dynamic Components</h1>
  3. <p>The Transition component wraps around the dynamic component so that the switching can be animated.</p>
  4. <button @click="toggleValue = !toggleValue">Switch component</button>
  5. <Transition mode="out-in">
  6. <component :is="activeComp"></component>
  7. </Transition>
  8. </template>
  9. <script>
  10. export default {
  11. data () {
  12. return {
  13. toggleValue: true
  14. }
  15. },
  16. computed: {
  17. activeComp() {
  18. if(this.toggleValue) {
  19. return 'comp-one'
  20. }
  21. else {
  22. return 'comp-two'
  23. }
  24. }
  25. }
  26. }
  27. </script>
  28. <style>
  29. .v-enter-active {
  30. animation: slideIn 0.5s;
  31. }
  32. @keyframes slideIn {
  33. from {
  34. translate: -200px 0;
  35. opacity: 0;
  36. }
  37. to {
  38. translate: 0 0;
  39. opacity: 1;
  40. }
  41. }
  42. .v-leave-active {
  43. animation: slideOut 0.5s;
  44. }
  45. @keyframes slideOut {
  46. from {
  47. translate: 0 0;
  48. opacity: 1;
  49. }
  50. to {
  51. translate: 200px 0;
  52. opacity: 0;
  53. }
  54. }
  55. #app {
  56. width: 350px;
  57. margin: 10px;
  58. }
  59. #app > div {
  60. border: solid black 2px;
  61. padding: 10px;
  62. margin-top: 10px;
  63. }
  64. </style>
实例 10

组件之间的切换设置动画。

  1. <template>
  2. <h1>The :css="false" Prop</h1>
  3. <p>With the 'css' prop set to 'false', we tell the compiler that JavaScript hooks are used instead of CSS transition classes.</p>
  4. <p>When we use :css="false", we must call done() inside the 'enter' and the 'leave' hooks, to tell the browser when those transitions are finished.</p>
  5. <button @click="pVisible=!pVisible">Toggle</button>
  6. <div>
  7. <Transition
  8. :css="false"
  9. @enter="onEnter"
  10. @after-enter="onAfterEnter"
  11. @before-leave="onBeforeLeave"
  12. @leave="onLeave"
  13. >
  14. <p
  15. v-if="pVisible"
  16. id="p1">
  17. Hello World!
  18. </p>
  19. </Transition>
  20. </div>
  21. </template>
  22. <script>
  23. export default {
  24. data() {
  25. return {
  26. pVisible: false
  27. }
  28. },
  29. methods: {
  30. onEnter(el,done) {
  31. let pos = 0;
  32. window.requestAnimationFrame(frame);
  33. function frame() {
  34. if (pos > 150) {
  35. done();
  36. } else {
  37. pos++;
  38. el.style.left = pos + "px";
  39. window.requestAnimationFrame(frame);
  40. }
  41. }
  42. },
  43. onAfterEnter(el) {
  44. el.style.backgroundColor = "yellow";
  45. },
  46. onBeforeLeave(el) {
  47. el.style.backgroundColor = "lightgreen";
  48. },
  49. onLeave(el,done) {
  50. let pos = 150;
  51. window.requestAnimationFrame(frame);
  52. function frame() {
  53. if (pos < 0) {
  54. done();
  55. }
  56. else {
  57. pos--;
  58. el.style.left = pos + "px";
  59. window.requestAnimationFrame(frame);
  60. }
  61. }
  62. }
  63. }
  64. }
  65. </script>
  66. <style>
  67. #p1 {
  68. position: absolute;
  69. padding: 10px;
  70. border: dashed black 1px;
  71. background-color: lightgreen;
  72. }
  73. #app > div {
  74. position: relative;
  75. background-color: coral;
  76. width: 300px;
  77. height: 300px;
  78. border: dashed black 1px;
  79. margin-top: 20px;
  80. }
  81. </style>
实例 11

使用 enterActiveClass prop 将 'v-enter-active' CSS 类重命名为 'entering'。

  1. <template>
  2. <h1>The 'enterActiveClass' Prop</h1>
  3. <button @click="this.exists = !this.exists">{{btnText}}</button><br>
  4. <Transition enter-active-class="entering">
  5. <p v-if="exists">Hello World!</p>
  6. </Transition>
  7. </template>
  8. <script>
  9. export default {
  10. data() {
  11. return {
  12. exists: false
  13. }
  14. },
  15. computed: {
  16. btnText() {
  17. if(this.exists) {
  18. return 'Remove';
  19. }
  20. else {
  21. return 'Add';
  22. }
  23. }
  24. }
  25. }
  26. </script>
  27. <style>
  28. .entering {
  29. background-color: lightgreen;
  30. animation: added 1s;
  31. }
  32. .v-leave-active {
  33. background-color: lightcoral;
  34. animation: added 1s reverse;
  35. }
  36. @keyframes added {
  37. from {
  38. opacity: 0;
  39. translate: -100px 0;
  40. }
  41. to {
  42. opacity: 1;
  43. translate: 0 0;
  44. }
  45. }
  46. p {
  47. display: inline-block;
  48. padding: 10px;
  49. border: dashed black 1px;
  50. }
  51. </style>

分类导航