菜单的下拉和收起动画,看起来好像比较简单,但是搞了半天。
最后可以使用的代码:
<transition
name="default"
v-on:enter="menuEnter"
v-on:leave="menuLeave"
enter-class="default-enter"
leave-to-class="default-leave-to"
>
<ul v-if="hasChildren(node, index)" class="menu-l2">
<li
v-for="(subnode, subIndex) in node.children"
:key="subnode.id"
@click.prevent="handleChildNodeClick(subnode, subIndex)"
class="node-l2"
:class="{ 'node-l2-active' : (subIndex == activeChildNodeIndex)}"
>{{ subnode.name }}</li>
</ul>
</transition>
js,这里是vue中的methods部分
menuEnter: function(el, done) {
//这行是关键
el.offsetWidth;
el.style.maxHeight = this.nodes[this.activeParentNodeIndex].children.length * 4 + "rem";
el.style.transition = "all 0.3s ease-in";
done();
},
menuLeave: function(el) {
el.offsetWidth;
el.style.maxHeight = 0;
el.style.transition = "all 0.3s ease-out";
}
css:
//transitions
.default-enter-active {
transition: all 0.3s ease-in;
}
.default-leave-active {
transition: all 0.3s ease-out;
}
.default-enter,
.default-leave-to {
max-height: 0;
}
说明
这里结合了js和css,其实只用js也可以,但是稍微麻烦。
只用css也可以,但是效果会稍微差一些(后面会解释)。
这里实现下拉和收起,利用的css的transition
。
vue
中定义了三个状态,对应显示,分别是(事件/css类)
before-enter
/v-enter
:动画开始/初始状态enter
/v-enter-active
:动画过程/中间状态after-enter
/v-enter-to
:动画结束/结束状态
对于隐藏(leave)也同样
计算过度高度
这里对于显示,利用的是max-height
属性,第一个关键点在于,初始状态max-height
设置为0,在中间状态设置为下拉框的实际大小。这样才会出现高度变化的下拉效果。
这也就是为什么使用js hook
,而不是直接使用css
的原因呢,因为子菜单高度无法确切知道,如果对效果不敏感,可以直接指定一个较大的max-height
,但是为了效果好,还是根据菜单长度确定过度最好。
控制页面刷新
第二个关键点在于 el.offsetWidth;
这一句看似无用的代码,不加的话,浏览器不会有动画效果,加上以后,按照网上的说法,会强制触发绘制。
其实在vue的官方文档有提到
When using JavaScript-only transitions, the done callbacks are required for the enter and leave hooks. Otherwise, the hooks will be called synchronously and the transition will finish immediately.
但是我测试了一下,调用done()
还是没有效果,不清楚原因。