block 和 none 問題 一些 CSS 屬性可以是動畫的,也就是說,當它的值改變時,它可以以平滑的方式改變。 做摺疊面板最簡單的方式是改變它的 block 或 none,這兩個屬性值不包含在可動畫屬性中。詳見:CSS animated properties。所以,設置 CSS 動畫(keyfr ...
block 和 none 問題
一些 CSS 屬性可以是動畫的,也就是說,當它的值改變時,它可以以平滑的方式改變。
做摺疊面板最簡單的方式是改變它的 block
或 none
,這兩個屬性值不包含在可動畫屬性中。詳見:CSS animated properties。所以,設置 CSS 動畫(keyframes)或 transition 都是沒有效果的。
JS 輔助實現
這個時候就需要藉助 JS 來實現摺疊面板。首先,獲取摺疊面板內容的高度,有了高度按照一定周期來逐步增加高度,或逐步減少高度。
<div class="l-expandable">
<div class="l-expandable__title">
<div @click="isToggled = !isToggled">
<div>
<slot name="icon" />
</div>
{{ text }}
</div>
<div @click="toggle">
<i-ep-arrow-down />
</div>
</div>
<div
@click="toggle"
:class="{ 'arrow-up': !isToggled, 'arrow-down': isToggled }">
<div class="arrow">
<i-ep-arrow-down />
</div>
</div>
</div>
setup
通過一個變數 isToggled
來判斷是否摺疊過面板。content
是獲取面板內容的模板引用,height
是高度,對模板引用的高度進行設置。
// setup
const isToggled = ref(true);
const content = ref();
const height = ref();
// 把每一個摺疊面板的高度都分成 10 份來間接性執行,執行完最後一幀之後設置 0,即不顯示內容
function toggleClose() {
let counter = 9;
let cHeight = height.value;
const interval = setInterval(() => {
cHeight -= height.value / 10;
content.value.style.height = `${cHeight.value}px`;
counter--;
if (counter == 0) {
content.value.style.height = `${0}px`;
// 已經摺疊了面板
isToggled.value = false;
clearInterval(interval);
}
}, 10);
}
function toggleOpen() {
let counter = 9;
let cHeight = 0;
const interval = setInterval(() => {
cHeight += height.value / 10;
content.value.style.height = `${cHeight}px`;
counter--;
if (counter == 0) {
content.value.style.height = `${height.value}px`;
// 已經打開了面板
isToggled.value = true;
clearInterval(interval);
}
}, 10);
}
function toggle() {
if (isToggled.value) {
toggleClose();
} else {
toggleOpen();
}
}
// 組件內容渲染完成之後,獲取模板引用對象,計算高度,並插入到 CSS 樣式中
onMounted(() => {
height.value = $(content.value).height();
content.value.style.height = `${height.value}px`;
});
CSS 動畫
.l-expandable__title {
border-left: 4px solid var(--el-color-primary);
}
.arrow {
transform: scale(0, 0);
}
.l-expandable__title:hover .arrow {
transform: scale(1, 1);
}
.arrow-up {
animation: arrow-up-animation 0.3s ease-in;
transform: rotate(180deg);
}
.arrow-down {
animation: arrow-down-animation 0.3s ease-in;
transform: rotate(0deg);
}
@keyframes arrow-up-animation {
@for $index from 0 to 10 {
#{$index * 10%} {
transform: rotate($index * 18deg);
}
}
}
@keyframes arrow-down-animation {
@for $index from 0 to 10 {
#{$index * 10%} {
transform: rotate(180deg - $index * 18deg);
}
}
}
.l-expandable__content {
overflow: hidden;
transition: var(--l-transition);
}