另外一种状况和Vue过渡执行过程见初始渲染javascript
new Vue({ el: '#app-2', data: { taxiCalled: false } })
<style> .slideInRight { transform: translateX(300px); } .go { transition: all 2s ease-out; } </style> <div id="app-2"> <button @click="taxiCalled = true"> Call a cab </button> <transition enter-class="slideInRight" enter-active-class="go"> <p v-if="taxiCalled">?</p> </transition> </div>
这个过程到底发生了什么?css
当点击按钮时,texiCalled
被设置成true
,而且taxi
插入页面。实际上在完成这些动做以前,Vue读取了指定的类enter-class
(这里为slideInRight
)并把它应用于taxi
的外包元素p
,而后指定类enter-active-class
的状况是同样的。vue
类enter-class
在第一帧以后就被移除了,类enter-active-class
也在动画结束时被移除。java
这种建立动画的方式成为FLIPgit
taxi
在屏幕的右边开始。taxi
在屏幕的最左边做为结束。transform
和opacity
在第一帧和最后帧两个状态之间扭转,这里使用translateX(300px)
使taxi
在两个状态间产生300px的位移。taxi
从右到左产生了300px的位移。new Vue({ el: '#app-3', data: { taxiCalled: false }, methods: { enter(el, done) { Velocity(el, { opacity: [1, 0], translateX: ["0px", "200px"] }, { duration: 2000, easing: "ease-out", complete: done }) }, leave(el, done) { Velocity(el, { opacity: [0, 1], 'font-size': ['0.1em', '1em'] }, { duration: 200, complete: done }) } } })
<div id="app-3"> <button @click="taxiCalled = true"> Call a cab </button> <button @click="taxiCalled = false"> Cancel </button> <transition @enter="enter" @leave="leave" :css="false"> <p v-if="taxiCalled">?</p> </transition> </div>
抓取enter
、leave
和appear
三种状况的时间点,每种状况上都定义了四种事件,总计12种事件(见API)。在这12种事件绑定钩子函数,这些函数能够配合CSS模式使用,也能够单独使用。这里咱们在钩子函数中使用velocity
脚本动画引擎,单独完成动画配置。github
在这些钩子中不必定非要使用
velocity
,可使用任何库。
上面代码中:css="false"
是告诉Vue,咱们关闭CSS的处理,这里能够节省点CPU时间,将跳过全部与CSS动画相关代码,单纯的使用Javascript动画。面试
咱们这里分别在@enter
和@leave
上绑定了钩子函数,他们将在taxi
被插入和离开时执行。函数的第一个参数为外包标签(这里是<p>
),第二个参数done
必须写,尽管你可能不去用它。这是由于用Javascript代替CSS,Vue没法识别动画何时完成,这里Vue会认为这个离开的动画在@leave
事件以前就完成了,因此执行leave
没有动画渲染。segmentfault
关于velocity
,这种类型的API,咱们称为forcefeeding,咱们只要往它的实例中填写数据,无论他内部如何运行。(具体查看velocity)api
在<transition>
使用appear
特性,在组件第一次被插入时执行相关的转换。
使用它会给人带来一种页面很快的加载大量元素的感受(错觉)。数组
这里还使用了<transition>
的name
特性,可使用自定义的类名('自定义过渡的类名'的类似写法),name-enter-active
、name-enter
等。
而使用这种自定义类名是一种好习惯。
<div id="app-4"> <transition appear name="flip"> <!-- 指定宽高由于是引用的图片,不知道尺寸 --> <img src="https://b-ssl.duitang.com/uploads/item/201602/20/20160220213530_Z4reH.thumb.700_0.jpeg" style="width:300px;height:300px"> </transition> <p>在组件第一次插入文档时执行相关的过渡</p> </div> <script> new Vue({ el: '#app-4' }) </script>
img { float: left; padding: 5px } .flip-enter-active { transition: all 5s cubic-bezier(0.55, 0.085, 0.68, 0.53); } .flip-enter { transform: scaleY(0) translateZ(0); opacity: 0; }
以上整个过程以下:
Vue发现appear
特性,开始查找<transition>
标签中的JavaScript钩子或指定的CSS类名。以后若是有一个name
被指定,就根据这个name
查到过渡的入口:mySpecifiedName-enter
、mySpecifiedName-enter-active
等。
若是以上过程失败,就会找默认的v-enter
、v-enter-active
等。
new Vue({ el:'#app-5', data:{ kisses: 0 } })
#app-5 button span { color: rgb(255, 0, 140); } #app-5 p { margin: 0; position: absolute; font-size: 3em; } .fade-enter-active { transition: opacity 5s } .fade-leave-active { transition: opacity 5s; opacity: 0 } .fade-enter { opacity: 0 }
<div id="app-5"> <button @click="kisses++"> <span>?</span>Kiss!</button> <transition name="fade"> <p v-if="kisses < 3">? frog</p> <p v-if="kisses >= 3">? princess</p> </transition> </div>
结果发现切换后没任何过渡效果,缘由是什么呢?
原来是,Vue会启动自身的优化系统,发现两个元素如出一辙,就是内容不一样,所以当切换时,只作了内容的替换,标签<p></p>
部分并无被替换,所以没有过渡的效果。
咱们能够为须要过渡的元素增长key
属性,让Vue识别青蛙与公主两个不一样的元素。以下:
<transition name="fade"> <p v-if="kisses < 1" key="frog">? frog</p> <p v-if="kisses >= 1" key="princess">? princess</p> </transition>
过渡效果正常了。
最佳实践,在元素上使用
key
,尤为是当元素间具备不一样的语义时。
在有两个以上元素时,你可能这么作:
<transition name="fade"> <p v-if="kisses < 2" key="frog">? frog</p> <p v-else-if="kisses >= 2 && kisses <=5" key="princess">? princess</p> <p v-else key="santa">? santa</p> </transition>
更好的方法是咱们能够根据已有的数据动态的处理多元素过渡。
new Vue({ el: '#app-6', data: { kisses: 0, kindOfTransformation: 'fade', transformationMode: 'in-out' }, computed: { transformation() { if (this.kisses < 3) { return 'frog' } if (this.kisses >= 3 && this.kisses <= 5) { return 'princess' } if (this.kisses > 5) { this.kindOfTransformation = 'zoom' this.transformationMode = 'out-in' return 'santa' } }, emoji() { switch (this.transformation) { case 'frog': return '?' case 'princess': return '?' case 'santa': return '?' } } } })
<div id="app-6"> <button @click="kisses++"><span>?</span>Kiss!</button> <transition :name="kindOfTransformation" :mode="transformationMode"> <p :key="transformation">{{emoji}} {{transformation}}</p> </transition> </div>
以上过渡的name
、mode
以及元素的key
和内容,都将根据实例数据与计算属性进行动态的绑定。
这样作更加的灵活,而且能够为不一样的元素应用不一样的过渡(name
和mode
的不一样)。
这里的mode
有三种状况:
再来看一个过渡模式的例子。
new Vue({ el: '#app-7', data: { product: 0, products: ['?umbrella', '?computer', '?ball', '?camera'] } })
#app-7 { margin-left:300px; } #app-7 p { position: absolute; margin: 0; font-size: 3em; } .slide-enter-active { transition: transform .5s } .slide-leave-active { transition: transform .5s; transform: translateX(-300px); } .slide-enter { transform: translateX(300px) }
<div id="app-7" style="margin-bottom:100px;"> <button @click="product++">next</button> <transition name="slide" > <p :key="products[product % 4]">{{products[product % 4]}}</p> </transition> </div>
这彷佛没什么问题。那么如今修改下CSS,去掉绝对定位。
#app-7 p { /* position: absolute; */ margin: 0; font-size: 3em; }
再来看看结果。
彷佛是有点不对劲,为何会这样呢?
看看过渡执行时的DOM,发现先后两个元素的过渡是同时进行,这是Vue的默认状况,即两个<p></p>
同时存在,若是不使用绝对定位,那么上一个就会把下一个的位置挤掉。
这下过渡模式mode
就派上用处了,咱们为过渡添加mode
属性为out-in
,旧先出新后进。
<transition name="slide" mode="out-in"> <p :key="products[product % 4]">{{products[product % 4]}}</p> </transition>
列表过渡的状况比较复杂。一个问题一个问题看吧。
当<transition>
中有多个并列的元素时,咱们又没有使用v-if|else
指令做用其上时,会警告咱们使用<transition-group>
标签代替它。
一组列表的过渡效果,由<transition-group>
包围,有几点比单元素过渡特殊的。
<transition-group>
上设置tag
属性外包多个元素,如<transition-group tag="p">
v-for
把元素渲染成列表形式:key="data"
标记以与它的同胞们做区分进入/离开
,多了一个移动,使用name-move
来定义类名(后面详细解释)。看个普通示例。
new Vue({ el: '#app-1', data: { items: [1, 2, 3, 4, 5, 6, 7, 8, 9], nextNum: 10 }, methods: { ramdomIndex: function () { return Math.floor(Math.random() * this.items.length) }, add: function () { this.items.splice(this.ramdomIndex(), 0, this.nextNum++) }, remove: function () { this.items.splice(this.ramdomIndex(), 1) } } })
.num-list-item { margin-right: 10px; } .num-list-enter, .num-list-leave-to { opacity: 0; transform: translateY(30px); } .num-list-enter-active, .num-list-leave-active { transition: all 2s; }
<div id="app-1"> <button @click="add">添加</button> <button @click="remove">移除</button> <transition-group name="num-list" tag="p"> <span v-for="item in items" :key="item" class="num-list-item"> {{item}} </span> </transition-group> </div>
很明显只有透明度的过渡,而Y轴(30px)的转换过渡没有成功,这是由于<span>
为一个inline
元素,没有这种转换过渡的功能,所以咱们须要把它切换成inline-block
元素。
.num-list-item { /*把span切换成inline-block*/ display:inline-block; margin-right: 10px; }
仔细观察下还能发现个问题,其余元素由于进入/离开
的那个元素,会发生位置的变化,其余元素这种移动变换没有过渡的效果,这不是咱们想要的。
如今就轮到使用name-move
为其余元素在此时添加移动过渡效果,很简单的作个修改:
.num-list-move,/* 为其余受到影响的元素添加移动过渡效果*/ .num-list-enter-active, .num-list-leave-active { transition: all 2s; }
观察文档结构,当进入/离开发生时,会给受影响的元素添加移动过渡类名num-list-move
。
进入过渡时没问题了,其余元素平滑的向右位移,可是离开过渡时其余受影响元素的移动仍是没有过渡效果,这是由于定位问题,离开的元素要有2秒消失,默认的static
定位要在2秒后离开元素才会腾出空位让给后面的元素,而此时此刻,移动过渡的时效2秒已通过去了,所以后面元素才会很突兀的补位。
那么,咱们能够在离开状态name-leave-active
上使用absolute
让元素脱离正常的文档流,那么一发生离开,后面的元素就能够开始正常的移动过渡了。以下修改:
/* 在此离开过渡的状态类名添加absolute定位,以受影响元素正常的使用平滑过渡 */ .num-list-leave-active { position: absolute; }
总结下,就是当使用行内元素时,使用位置转换的过渡须要把其设置为inline-block
,不然位置转换没有效果。在离开/进入过渡时,受影响的其余元素应该使用移动过渡name-move
为期定义移动过渡。还须要在离开过渡状态类中name-leave-active
设置离开过渡元素的定位为absolute
使其脱离正常的文档流,以不妨碍其余元素的移动过渡。
观察其上CSS代码,能够发现<span>
的进入/离开/移动过渡定义的过渡都同样,即:
.num-list-move, .num-list-enter-active, .num-list-leave-active { transition: all 2s; }
所以能够去掉这些过渡状态类名,之间写在<span>
的样式里就能够了。
.num-list-item { display:inline-block; margin-right: 10px; /* 代替 进入/离开/移动过渡状态类 */ transition: all 2s; } .num-list-enter, .num-list-leave-to { opacity: 0; transform: translateY(30px); } /* .num-list-move, .num-list-enter-active, .num-list-leave-active { transition: all 2s; } */ /* 在此离开过渡的状态类名添加absolute定位,以受影响元素正常的使用平滑过渡 */ .num-list-leave-active { position: absolute; }
其余都原封不动,效果和第一种同样。
在第一种写法的基础上添加如下功能,再次理解移动过渡。
js中导入lodash库,使用shuffle
方法从新排列数组items
。
methods: { ramdomIndex: function () { return Math.floor(Math.random() * this.items.length) }, add: function () { this.items.splice(this.ramdomIndex(), 0, this.nextNum++) }, remove: function () { this.items.splice(this.ramdomIndex(), 1) }, //使用`shuffle`方法从新排列数组`items`。 shuffle: function () { this.items = _.shuffle(this.items) } }
页面添加一个按钮
<div id="app-1"> <button @click="add">添加</button> <button @click="remove">移除</button> <button @click="shuffle">重排列</button> <transition-group name="num-list" tag="p"> <span v-for="item in items" :key="item" class="num-list -item"> {{item}} </span> </transition-group> </div>
一个汽车站:
new Vue({ el: '#app-2', data: { buses: [1, 2, 3, 4, 5], nextBus: 6 }, mounted() { setInterval(() => { const headOrTail = () => Math.random() >= 0.5 if (headOrTail()) { this.buses.push(this.nextBus) this.nextBus += 1 } else { this.buses.splice(0, 1) } }, 2000) } })
.station-bus { display: inline-block; margin-left: 10px; font-size: 2em; } .station-enter { opacity: 0; transform: translateX(30px); } .station-leave-to { opacity: 0; transform: translateX(-30px); } .station-move, .station-enter-active, .station-leave-active { transition: all 2s; } .station-leave-active { position: absolute; }
<div id="app-2"> <h3>公交车站</h3> <transition-group tag="p" name="station"> <span v-for="bus in buses" :key="bus" class="station-bus">?</span> </transition-group> {{buses}} </div>
在元素插入时的钩子上定义一个timer
,每隔两秒一辆车进入或离开,为它们设置进入/离开过渡,和其余受影响车辆的移动过渡。
若是想要在咱们的站点的各处重用一种过渡,把它包装进一个组件是个好方法。
要在站点上展现/隐藏一些缩略的文章,咱们能够编写一个过渡组件,而后为不一样的缩略文章添加这个过渡组件使其拥有过渡效果。
Vue.component('puff', { functional: true, render: function (createElement, context) { var data = { props: { 'enter-active-class': 'magictime puffIn', 'leave-active-class': 'magictime puffOut' } } return createElement('transition', data, context.children) } }) new Vue({ el: '#app-3', data: { showRecipe: false, showNews: false } })
<link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/magic/1.1.0/magic.min.css"> <div id="app-3"> <button @click="showRecipe = !showRecipe"> Recipe </button> <button @click="showNews= !showNews"> Breaking News </button> <puff> <article v-if="showRecipe" class="card"> <h3> 过渡和动画 </h3> <p> 自定义过渡的类名 在钩子中使用Velocity 两个元素之间的过渡 ... </p> </article> </puff> <puff> <article v-if="showNews" class="card"> <h3> 今日头条 </h3> <p> 201七、2018面试分享(js面试题记录)记得点赞分享哦;让更多的人看到~~ </p> </article> </puff> </div>
这里使用了一个magicCSS动画库和函数式组件(todo)。
定义一个全局的函数式组件。在其render
选项中定义函数并返回一个可重用的元素<puff>
,在内部经过magic将进入/离开的过渡效果添加到<puff>
的属性上。在页面须要的地方包裹该<puff>
元素便可。
响应是Vue永恒的主体,所以过渡和它的属性均可以是动态的。这样咱们能够控制在特定的位置与时间使用特定的过渡。
在多个元素之间的过渡和过渡模式中咱们已经展现了动态过渡,对于不一样的<p>
元素,咱们使用了不一样的过渡效果和模式。