动画过渡与表单的数据绑定
动画过渡
单一元素/组件的过渡
概念:Vue 在元素显示隐藏时, 提供了 transition 的封装组件,可以给任何元素和组件添加进入/离开过渡
语法:<transition name="过渡css名">单一标签</transition>
被transition标签包裹的元素在其显示隐藏时,六个不同的阶段会自动添加6个class名:
元素进入的第一帧 [transition name 属性]-enter
元素进入中 [transition name 属性]-enter-active
元素进入的最后一帧 [transition name 属性]-enter-to
元素离开的第一帧 [transition name 属性]-leave
元素离开中 [transition name 属性]-leave-active
元素离开的最后一帧 [transition name 属性]-leave-to
<div id="app">
<!--
被transition标签包裹的元素在其显示隐藏时,六个不同的阶段会自动添加6个class名
元素进入的第一帧 [transition name 属性]-enter
元素进入中 [transition name 属性]-enter-active
元素进入的最后一帧 [transition name 属性]-enter-to
元素离开的第一帧 [transition name 属性]-leave
元素离开中 [transition name 属性]-leave-active
元素离开的最后一帧 [transition name 属性]-leave-to
-->
<transition name="fade">
<div class="box" v-if="show"></div>
</transition>
<button @click="show = !show">show/hidden</button>
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
show: true
}
})
</script>
<style>
.box {
width: 100px;
height: 100px;
background-color: green;
}
.fade-enter, .fade-leave-to{
/*元素进入动画第一帧 类名*/
opacity: 0;
transform: translateX(300px);
width: 0;
height: 0;
}
.fade-enter-active, .fade-leave-active {
/*元素进入整个过程中 类名*/
transition: all .5s linear;
}
.fade-enter-to, .fade-leave {
/*元素进入动画最后一帧 类名*/
opacity: 1;
transform: translateX(0);
width: 100px;
height: 100px;
}
</style>
注意:
- transition 标签内只能包含一个直接子元素
- transition 标签的name属性可以省略, 省略后元素进入离开时类名默认使用v-开头,例: v-enter v-enter-to v-leave v-leave-to。
列表的进入/离开过渡
概念: 因为transition标签内部只能监听单个节点,或同一时间渲染多个节点中的一个。所以transition标签内部无法监听多个节点的。所以vue提供了 <transition-group>
组件实现内部监听多个节点的进入离开
语法: <transition-group name="过渡css名">多个标签</transition-group>
<div id="app">
<transition-group name="fade">
<p v-for="c in arr" :key="c">{{c}}</p>
</transition-group>
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
arr: ['a', 'b', 'c', 'd', 'e']
}
})
</script>
<style>
.fade-enter,
.fade-leave-to {
transform: translateX(100px);
}
.fade-enter-active,
.fade-leave-active {
transition: all .5s linear;
}
.fade-enter-to,
.fade-leave {
transform: translateX(0);
}
</style>
注意:
- 在transition-group组价中,列表渲染的key不能使用index。index作为key在添加或者删除时会导致不是正确的DOM节点被移除
<!--
请观察下面代码,
我们在使用 transition-group 元素时内部兄弟节点使用数组每一项的值作为key ':key="c"' (因为key值必须唯一所以请不要让数组中包含相同的值),
这时请尝试添加或删除数组中的某一项,你胡发现动画效果是正确的
但是如果我们使用数组下标作为key后,元素的添加或删除动画会异常
-->
<div id="app">
<transition-group name="fade">
<p v-for="(c,i) in arr" :key="i">
{{c}}
<button @click="removeArr(c)">remove</button>
</p>
</transition-group>
</div>
<script>
let vm = new Vue({
el: '#app',
data: {
arr: ['a', 'b', 'c', 'd', 'e']
},
methods: {
removeArr(c) {
this.arr = this.arr.filter(a => a !== c)
}
}
})
</script>
<style>
.fade-enter,
.fade-leave-to {
transform: translateX(100px);
}
.fade-enter-active,
.fade-leave-active {
transition: all .5s linear;
}
.fade-enter-to,
.fade-leave {
transform: translateX(0);
}
</style>
- transition-group会被vue默认渲染成一个span元素,如果你想设置transition-group组件渲染输出的节点可以设置其tag属性,例 tag="ul" transition-group组件就会被渲染成ul标签
<transition-group tag="ul" name="move">
<li v-for="todo in todos" :key="todo.id">
{{todo.text}}
</li>
</transition-group>
JavaScript 动画钩子
概念:无论transition 和 transition-group 标签都支持使用动画js钩子。动画的钩子就是元素在整个动画过渡中默写特殊的时间点。当动画到达这些时间点时就会触发js钩子所绑定方法。有了js动画钩子就可以实现一些复杂的动画。
语法:动画钩子就是vue提供的一些自定义事件,监听响应的事件触发绑定的函数
<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>
<transition-group
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-group>
注意:动画钩子事件会将当前动画元素作为参数传入到函数内部
<!--
Velocity 和 jQuery.animate 的工作方式类似,也是用来实现 JavaScript 动画的一个很棒的选择
-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter">
<div v-show="show" class="box"></div>
</transition>
<button @click="show = !show">show/hidden</button>
</div>
<script>
console.log(Velocity)
new Vue({
el: "#app",
data: {
show: true
},
methods: {
beforeEnter(el) {
console.log(el, '元素将要显示')
el.style.opacity = 0
},
enter(el, done) {
console.log('元素正在显示')
Velocity(el, {
opacity: "1",
translateY: '30px',
}, {
duration: 400,
easing: "swing",
loop: 2,
complete: done // 动画结束后触发done表示自定义动画结束
});
},
afterEnter(el) {
el.style.opacity = 1
// console.log(el.offsetHeight)
console.log('元素显示完毕')
}
}
})
</script>
注意:动画钩子事件会中enter事件与leave事件除了会默认传递第一个参数el(当前动画元素)以外,还会传递第二个参数 done,done 是一个方法用来告诉Vue当前元素的自定义动画已经结束,动画钩子才会执行下一步 after-enter 或者after-leave。如果自定义动画效果结束后不调用done可能会导致动画元素样式不更新。
表单的数据绑定
概念: 在开发中我们经常需要创建一些表单元素,表单元素的内容由vue实例中的data控制。而用户在表单上的输入操作又可以修改实例中的数据。上面的行为我们称之为表单的双向绑定。
例子: 下面的代码就是实现input表单的双向绑定
<div id="app">
<input v-bind:value="str" @input="inputHandel">
{{str}}
</div>
<script>
new Vue({
el: "#app",
data: {
str: 'hello world'
},
methods: {
inputHandel(evt) {
this.str = evt.target.value
}
}
})
</script>
注意:
- Vue为了简化表单元素的数据绑定, input 、select、 textarea 这些值绑定全部统一使用value属性,但是单复选框依然是checked属性
<div id=app>
<input v-bind:value="str" @input="inputHandel">
{{str}}
<textarea :value="str"></textarea>
<select :value="city">
<option value="bj">北京</option>
<option value="sh">上海</option>
<option value="gz">广州</option>
<option value="sz">深圳</option>
</select>
<input type="checkbox" :checked="true">
</div>
<script>
new Vue({
el: "#app",
data: {
str: 'hello world',
city: 'sz'
},
methods: {
inputHandel(evt) {
this.str = evt.target.value
}
}
})
</script>
- 虽然Vue帮我们简化了表单元素值得绑定,但是在不同表单元素之间依然有一些差异的。要根据不同的表单元素差异绑定不同的属性与方法还是有些繁琐。这时Vue为了帮我们解决表单的差异,提供了一个v-model的指令,v-model本质上是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
语法:v-model="data属性"
<div id="app">
<input v-model="str">
{{str}}
<textarea v-model="str"></textarea>
<p>{{city}}</p>
<select v-model="city">
<option value="bj">北京</option>
<option value="sh">上海</option>
<option value="gz">广州</option>
<option value="sz">深圳</option>
</select>
<p>{{agree? '同意' : '不同意'}}</p>
<input type="checkbox" v-model="agree">
</div>
<script>
new Vue({
el: "#app",
data: {
str: 'hello world',
city: 'sz',
agree: false
}
})
</script>
注意: v-model 绑定支持表单元素的复选时,v-model绑定的数据请使用数组类型
<div id="app">
<!-- 选择器当设置了可复选属性时,会自动将v-model中的数据转化为数组 -->
<select v-model="city" multiple>
<option value="" disabled>请选择你所在的城市</option>
<option value="bj">北京</option>
<option value="sh">上海</option>
<option value="gz">广州</option>
<option value="sz">深圳</option>
</select>
<!--复选框要实现复选时,必须将v-model中的数据设置为数组-->
<p>爱好{{hobbys}}</p>
<label>
抽烟
<input type="checkbox" value="抽烟" v-model="hobbys">
</label>
<label>
喝酒
<input type="checkbox" value="喝酒" v-model="hobbys">
</label>
<label>
烫头
<input type="checkbox" value="烫头" v-model="hobbys">
</label>
<label>
玩摇滚
<input type="checkbox" value="玩摇滚" v-model="hobbys">
</label>
</div>
<script>
new Vue({
el: "#app",
data: {
city: '',
hobbys: []
}
})
</script>
修饰符
.lazy
在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转为在 change 事件之后进行同步:
<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg">
.number
如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:
<!--
即使表单元素设置的type="number"属性但是表单的value依然是字符串
-->
<input v-model.number="age" type="number">
注意: 这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值。
.trim
如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:
<input v-model.trim="msg">