菜單的下拉和收起動畫,看起來好像比較簡單,但是搞了半天。
最后可以使用的代碼:
<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()
還是沒有效果,不清楚原因。