进入/离开 & 列表过渡 transition 和 animation (上):使用过渡和动画的3种方式

本文介绍了Vue中插入、更新或移除DOM时如何实现过渡效果,包括CSS过渡和动画的区别,以及通过CSS、自定义类名和JavaScript钩子的三种实现方式。详细讲解了如何使用`<transition>`组件结合`name`属性、Animate.css库以及声明钩子函数来创建各种过渡效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Vue 在插入、更新或者移除 DOM (就是添加、移除元素节点) 时,提供多种不同方式的应用过渡效果。
包括以下工具:

  • CSS 过渡 和动画中自动应用 class (在 <style> 中定义 css 效果)
  • 过渡钩子函数中使用 JavaScript 直接操作 DOM (methods 里面直接定义函数,并在<transition> 组件中用v-on 绑定 过渡状态 和methods  里的函数
  • 可以配合使用第三方 CSS 动画库,如 Animate.css
  • 可以配合使用第三方 JavaScript 动画库,如 Velocity.js 

过渡的类名:

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

  1. 1. v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。

  2. 2. v-enter-active定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。

  3. 3. v-enter-to: 2.1.8版及以上 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。

  4. 4. v-leave: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。

  5. 5. v-leave-active定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。

  6. 6. v-leave-to: 2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。

过渡的名字 name:

如果你使用一个前没有名字的 <transition>,则 v- 是这些类名的默认缀。如果你使用了 <transition name="my-transition">,那么 v-enter 会替换为 my-transition-enter。 

动画animation 与过渡 transition 的区别

在动画中 v-enter 类名在节点插入 DOM 后不会立即删除,而是在 animationend 事件触发时删除。

使用过渡 transtion和 动画animation 的3种方式:

 一、使用CSS (使用 name 特性,绑类名 css 样式)

1. 在<style>标签里设置过渡的类的样式(6 个 class:v-enter-active,v-enter,v-enter-to;v-leave,v-leave-to,v-leave-active)

2. 在 <transition> 组件里 使用 name 特性,绑类名 css 样式(例如:  <transition name="change">,<style> </style> 里面的样式名应绑定为 change-enter-active,change-enter,change-enter-to;change-leave,change-leave-to,change-leave-active)。(例如:<transition name="change">)

<style>
.change-enter{
	color:red;
	font-size:20px;
	transition:2s;
}

.change-enter-to{
	color:blue;
	font-size:58px;
	transition:5s;
}

.change-enter-active{
	transition-timing-function:ease;
}

.change-leave-active{
	color:yellow;
	font-size:2px;
	transition:3s;}

</style>
<body>
<div id="demo">
	<button v-on:click="show=!show">love?</button>
		<transition name="change">
			<div v-if="show">{{mes}}</div>
		</transition>
</div>
<script>
new Vue({
el:"#demo",
data:{
	show:true,
	mes:"I love you!"
}
})
</script>

渲染效果:


二、自定义类名(优先级高于普通的类名,一般用于第三方库。如 Animate.css 。不用于库也可以 -可直接绑定css 样式。具体见下文 3.  )

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

1. 在<link>中引用文件

<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">

<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">

2.在<transition> 组件中绑定类名和第三方库中的样式名

<transition
    name="custom-classes-transition"
    enter-active-class="animated tada"
    leave-active-class="animated bounceOutRight"
  >

完整案例

使用第三方库 Animate.css示例代码:

  • 
     <div id="demo">
     <button @click="show = !show">
       love?
     </button>
      
    <transition
         enter-active-class="animated flip"
         leave-active-class="animated hinge">
    
    <div v-if="show">{{MES}}</div>
    </transition>
    </div>
     
    <script>
    
    new Vue({
        el: '#demo',
        data:{ 
            MES:"I LOVE YOU",
        	show:true
      }
    })
    
    </script>
    

    渲染效果:

  • 3.  不用于库也可以 -可直接绑定css 样式。优先于name+css 。

  • 例子解析:这里的enter-class绑定了enter-to 这个自定义样式绑定了,比 name+css 的样式 change-enter-to 优先级更高。(所以enter-to 这个样式生效,change-enter-to 这个样式不显示)

  • <transition name="change"
        enter-class="enter-to">
    <style>
    .change-enter-to{
    	color:red;
    	font-size:20px;
    	transition:2s;
    }
    
    .enter-to{
    	color:blue;
    	font-size:58px;
    	transition:5s;
    }
    </style>

     


三、声明钩子

1. 在vue 实例methods 中定义函数

2. 在<transition> 组件中 绑定钩子和函数  <transition v-on:before-enter="beforeEnter">

注意:

1)需要使用done回调函数防止过渡钩子同时进行。当只用 JavaScript (就是使用JS函数的方法过渡,而不使用CSS过渡)过渡的时候,在 enter 和 leave 中必须使用 done 进行回调。否则,它们将被同步调用,过渡会立即完成。也就是说,使用了done 回调函数,钩子才能一个一个按顺序执行,不会同步执行(同步执行时,值看到最后顺序的钩子的执行效果)

2) v-bind:css="false"屏蔽CSS效果(使钩子函数不受<style>里面的CSS效果影响)。对于仅使用 JavaScript 过渡的元素添加 v-bind:css="false",Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。

<transition
  v-on:before-enter="beforeEnter"
  v-on:enter="enter"
  v-on:after-enter="afterEnter"
  v-on:enter-cancelled="enterCancelled"

  v-on:before-leave="beforeLeave"
  v-on:leave="leave"
  v-on:after-leave="afterLeave"
  v-on:leave-cancelled="leaveCancelled"
>
  <!-- ... -->
</transition>

<script>

new Vue({
  el: '#demo',
  data: {
    show: true
  },
methods: {
  // --------
  // 进入中
  // --------

  beforeEnter: function (el) {
    // ...
  },
  // 当与 CSS 结合使用时
  // 回调函数 done 是可选的
  enter: function (el, done) {
    // ...
    done()
  },
  afterEnter: function (el) {
    // ...
  },
  enterCancelled: function (el) {
    // ...
  },

  // --------
  // 离开时
  // --------

  beforeLeave: function (el) {
    // ...
  },
  // 当与 CSS 结合使用时
  // 回调函数 done 是可选的
  leave: function (el, done) {
    // ...
    done()
  },
  afterLeave: function (el) {
    // ...
  },
  // leaveCancelled 只用于 v-show 中
  leaveCancelled: function (el) {
    // ...
  }
}
})
</script>

 


apper 特性: 初始渲染的过渡

(就是给元素设置第一次渲染的过渡效果)

一、apper 特性

<transition
  appear
  appear-class="custom-appear-class"
  appear-to-class="custom-appear-to-class" (2.1.8+)
  appear-active-class="custom-appear-active-class"
>
  <!-- ... -->
</transition>

二、可定义CSS 类名:

<transition
  appear
  appear-class="custom-appear-class"
  appear-to-class="custom-appear-to-class" (2.1.8+)
  appear-active-class="custom-appear-active-class"
>
  <!-- ... -->
</transition>

三、自定义 JavaScript 钩子:

<transition
  appear
  v-on:before-appear="customBeforeAppearHook"
  v-on:appear="customAppearHook"
  v-on:after-appear="customAfterAppearHook"
  v-on:appear-cancelled="customAppearCancelledHook"
>
  <!-- ... -->
</transition>

 


 多元素过渡 

一、用 v-if   v-else 语句 (需要设置 key 特性,让 vue 区分不同的元素)

<div id="demo">

<transition 
    v-on:before-enter="beforeEnter"
    v-on:enter="enter"
    v-on:leave="leave">
	
  <button v-if="isEditing" key="save" v-on:click="isEditing=!isEditing" >
    Save
  </button>
  <button v-else key="edit"  v-on:click="isEditing=!isEditing">
    Edit
  </button>
  
</transition>
</div>
    
<script>
new Vue({
  el: '#demo',
  data:{ 
	isEditing:true
  },
	methods:{
    	beforeEnter: function (el) {
     	......
    }}
})
</script>

二、通过给同一个元素的 key 特性设置不同的状态来代替 v-if 和 v-else

解析:

1. 用 computed 计算属性(详细可参考:计算属性原理),

     a. 创建了 buttonMessage 这个计算属性。这个buttonMessage 计算属性,依赖于 docState,随着 docState 的值的 变化 而变化。响应 docState 值的变化。

     b. computed 计算属性需要 getter  和 setter 两个函数。(详细可参考:computed计算属性设置setter computed 的get 和set),

2. 使用 switch  case 条件语句

<transition
    v-on:before-enter="beforeEnter"
    v-on:enter="enter"
    v-on:leave="leave"
    v-bind:css="false">

  <button v-bind:key="docState" v-on:click="change">
    {{ buttonMessage }}
  </button>
</transition>
</div>
 
<script>
new Vue({
  el: '#demo',
  data:{ 
  	docState:"saved"
  },
  computed: {
 	 buttonMessage: {
	 	get:function (){
			switch (this.docState) {
     		case 'saved': return 'Edit'
     		case 'edited': return 'Save'
     		case 'editing': return 'Cancel'
    }},
		set:function(){
			}}
	}

完整案例代码 

<body>
<div id="demo">
<transition
    v-on:before-enter="beforeEnter"
    v-on:enter="enter"
    v-on:leave="leave"
    v-bind:css="false">

  <button v-bind:key="docState" v-on:click="change">
    {{ buttonMessage }}
  </button>
</transition>
</div>
 
<script>
new Vue({
  el: '#demo',
  data:{ 
  	docState:"saved"
  },
  computed: {
 	 buttonMessage: {
	 	get:function (){
			switch (this.docState) {
     		case 'saved': return 'Edit'
     		case 'edited': return 'Save'
     		case 'editing': return 'Cancel'
    }},
		set:function(){
			}}
	},
	methods:{
		change:function(){		
			switch (this.buttonMessage) {
      		case 'Edit': this.buttonMessage='Save'
			this.docState='edited'
			break;
     		case 'Save': this.buttonMessage='Cancel'
			this.docState='editing'
			break;
     		case 'Cancel': this.buttonMessage= 'Edit'
			this.docState='saved'
			break;}
	},
		beforeEnter: function (el) {
     	 el.style.opacity =0.5
    	  el.style.transformOrigin = 'left'

	  Velocity(el, { opacity: 1, fontSize: '5em' }, { duration: 100 })
	  	  el.style.color='red'
    },
    enter: function (el, done) {
	  el.style.color='black'
	el.style.backgroundColor='yellow'
      Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 })
      Velocity(el, { fontSize: '1em' }, { complete: done })
    },
    leave: function (el, done) {
	  el.style.color='red'
      Velocity(el, { translateX: '15px', rotateZ: '50deg' }, { duration: 600 })
      Velocity(el, { rotateZ: '100deg' }, { loop: 2 })
      Velocity(el, {
        rotateZ: '45deg',
        translateY: '30px',
        translateX: '30px',
        opacity: 0
      }, { complete: done })
    }
  }
})
</script>

渲染效果


多个组件过渡:动态组件 <component is="">

 <div id="demo">
<ul>
<li v-on:click="currentTab = 'a'">a</li>
<li v-on:click="currentTab = 'b'">b</li>
<li v-on:click="currentTab = 'c'">c</li>
</ul>
  <transition	appear
	mode="out-in"	
	v-on:before-enter="beforeEnter"
    v-on:enter="enter"
    v-on:leave="leave"
    v-bind:css="false">

	<component v-bind:is="which"></component>

</transition>
</div>

 完整代码:

 <script src="https://unpkg.com/vue/dist/vue.js"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>

</head>

<body>

 <div id="demo">
<ul>
<li v-on:click="currentTab = 'a'">a</li>
<li v-on:click="currentTab = 'b'">b</li>
<li v-on:click="currentTab = 'c'">c</li>
</ul>
  <transition	appear
	mode="out-in"	
	v-on:before-enter="beforeEnter"
    v-on:enter="enter"
    v-on:leave="leave"
    v-bind:css="false">

	<component v-bind:is="which"></component>

</transition>
</div>
 
<script>

new Vue({
  el: '#demo',
  data:{ 
	currentTab:"a"
  },
  components:{
  	acom:{ 
		template:`
				<div>这里是组件A</div>
			`
		},
  	bcom:{ 
		template:`
				<div>这里是组件B</div>
			`
		},
	ccom:{ 
		template:`
				<div>这里是组件C</div>
			`
		}
  },
	computed:{
		which:{
		get:function(){
			return this.which=this.currentTab+"com"
			},
		set:function(){}}},
	methods:{
    	enter: function (el, done) {
	 			 el.style.color='black'
		 		el.style.backgroundColor='yellow'
     	 		Velocity(el, { opacity: 1, fontSize: '1.4em' }, { duration: 300 })
     			 Velocity(el, { fontSize: '1em' }, { complete: done })
    }
}
})

</script>

渲染效果:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值