Vue 的 transition & 实现路由类 Tab 左右滑动切换的效果
先说下 transition 为什么可以让你定义了 v-enter, v-enter-active 之后就可以触发transition.
实际过程按我的理解应该是这样:
最开始的时候, 先给元素加上了 v-enter, v-enter-active 两个class
在下一帧的时候, 删掉 v-enter 这个class.
在过渡结束之后, 再删掉 v-enter-active.
对于2的解释:
这个帧的概念我刚开始没理解, 但是实际上可以理解成 ‘一段时间之后’;
你删掉 v-enter 会导致什么情况?
元素的样式会变化
那么元素现在只有定义在自己身上的样式了
所以要变回去
然后发现有定义在 v-enter-active 上面的过渡效果
就会应用过渡效果
复盘一下过程:
刚开始的时候, 元素有自己的样式, 有 v-enter, v-enter-active 中定义的样式.
在一段时间间隔, 比如说 100ms 之后, 删掉了 v-enter, 意味着元素的样式变化。
变化, 然后发现存在 transition的定义, 就应用 transition,
这就形成了我们最终看见的 transition效果.
给个demo:
.box {
height: 100px;
width: 100px;
background: red;
}
.v-enter {
height: 200px;
width: 200px;
}
.v-enter-active {
transition: all 2s;
}
<div class="box"></div>
var box = document.querySelector('.box');
box.classList.add('v-enter');
box.classList.add('v-enter-active');
setTimeout(function(){
box.classList.remove('v-enter');
}, 100);
再说下元素从显示到消失的时候
我们知道, 如果过渡效果的终点是 display:none, 那么过渡效果是不会生效的, 所以我觉得
v-if, v-show 对过渡效果都是有所控制的, 它们应该是在 transitionEnd 的时候才去应用
display:none; 或者移除元素.
但是元素离开和元素进入还有点不一样:
你的元素在离开的时候, 元素是已经存在于页面上, 是你已经能看见的
然后你给它加上 v-leave, v-leave-active
按照之前的逻辑, v-leave 有样式, v-leave-active 里面有 transition
元素的样式变更-> 触发 transition, 所以加上就直接触发 transition, 还等个什么下一帧。
既然加上就触发 transition, 那你要 v-leave 干嘛, 为什么不直接把
样式和 transition 都加在 v-leave-active 上面呢。
暂时没有发现 v-leave 的作用, 感觉不是必须的, 就是对称一样. 或者为了样式分离.
transition 过渡效果的样式实际上是有通用规律的:
.v-enter {
opacity:0;
}
.v-enter-active {
transition: all 1s;
}
.v-leave-active {
opacity: 0;
transition: all 1s;
}
更常见的写法是这样:
.v-enter {
opacity:0;
}
.v-enter-active, .v-leave-active {
transition: all 1s;
}
.v-leave-active {
opacity: 0;
}
关于 transition 的一个应用: 实现路由 类 Tab 左右滑动切换的效果.
类 Tab 左右滑动切换的效果,就是这样:
[viewport]
[router1] [router2]
router1,2 左右拉来拉去, 就是左右滑动切换的效果.
那我们的问题就可以变成:
把当前的路由 router1, 和下一个路由 router2, 并列放到一行,
用户点击跳转的的时候, 同时滑动 router1,router2
但是直觉都告诉我们, 路由压根就不是这么排列的, 当你在 router1 的时候, router2 根本就不存在啊.
不存在何来并排.
但是想想我们之前说的, 加上 transition 之后, 元素消失会怎么办?
元素先应用 transition, transitionEnd 的时候, 元素才会消失.
这个的意思就是说:
跳转的一瞬间, 当前的 route1 会开始应用 transition, 还没有消失于页面之上.
然后下一个 router2 已经创建, 已经存在于页面之上.
此时, 两个 router 都是存在的.
这就是时机.
先说下我们的 DOM 结构
<transition>
<router-view></router-view>
<transition>
我们要先让两个 router 并排, how ?
定位啊:
.router-view {
position: absolute;
top: 0;
left: 0;
}
按理说两个 router 都是 left:0, top:0, 应该是重叠的是不是?
对.
所以我们对要进入的那个 router 做下 translate
比如当前的是 router1, 点击要向前跳转到 router2, 那么对 router2 translateX(100%);
这样两个 router 是不是就并排了.
并排之后就是动起来, 也就是应用滑动效果.
两个组件都是被 transition 包裹起来所以只要定义相应的class就可以了.
先说向前进:
假设 transition 的 name = ‘slide-forward’
对于 router1:
start: 是当前的位置
end: 是当前位置的 translateX(-100%), 也就是当前位置的左边.
对于 router2:
start: 是相对于当前位置的 translateX(100%);
end: 是当前位置.
// router1
.slide-forward-leave-active {
transition: all 1s;
transform: translate(-100%);
}
// router2
.v-enter {
transform: translateX(100%);
}
.v-enter-active {
transition: all 1s;
}
再说后退, 设 transition 的 name = ‘slide-back’;
class就是:
// router1
.v-enter {
transfrom: translateX(-100%);
}
// router2
.v-enter-active {
transition: all 1s;
}
.v-leave-active {
transfrom: translateX(100%);
transition: all 1s;
}
整理一下, 得到的两个 transition,
.slide-forward-enter {
transform: translate(100%);
}
.slide-forward-enter-active {
transition: all 1s ease-in-out;
}
.slide-forward-leave-active {
transform: translate(-100%);
transition: all 1s ease-in-out;
}
.slide-back-enter {
transform: translate(-100%);
}
.slide-back-enter-active {
transition: all 1s ease-in-out;
}
.slide-back-leave-active {
transform: translate(100%);
transition: all 1s ease-in-out;
}
现在再讨论一下: 我们该如何判断当前是前进还是后退呢?
下面这样:
watch: {
'$route' (to, from) {
if (!this.map[to.path]) {
this.map[to.path] = +new Date() + 1;
}
if (!this.map[from.path]) {
this.map[from.path] = +new Date();
}
if (this.map[to.path] > this.map[from.path]) {
this.transitionName = 'slide-forward';
} else {
this.transitionName = 'slide-back'
}
}
}