這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
不定高度展開收起影片
最近在做需求的時候,遇見了元素高度展開收起的影片需求,一開始是想到了使用 transition: all .3s; 來做影片效果,在固定高度的情況下,transition 影片很好使,滿足了需求,但是如果要考慮之后可能還會有更改的情況下,如果每次都是用固定高度來做影片,會顯得很繁瑣,也很呆,就想到了使用 height: auto; 來做高度影片,但是,眾所周知,高度設定成 auto 時是不會觸發 transition 影片的
.container {
height: 0;
background-color: #ccc;
overflow: hidden;
transition: all .3s;
}
.container:hover {
height: 1000px;
}
效果如圖,不能滿足影片的要求
在一番查找實驗之后,目前發現了如下幾種方法:
1. max-height 最大高度
transition 影片可以回應 max-height
.container {
max-height: 0;
background-color: #ccc;
overflow: hidden;
transition: all .3s;
}
.container:hover {
max-height: 1000px;
}
但是使用 max-height 做影片有一個問題,如果設定的最大高度越大,但是實際高度確與最大高度相差甚遠,那么整體的影片速度就會非常快,影片的時間只會是 實際高度 / 最大高度 * 影片時間,因為展開影片原本預期高度是設定的最大高度,所以整體時間是以最大高度完全展開所用時間來進行的,但是當到達實際高度的時候影片就停止了,所以最終影片時間會與期望時間相差甚遠,
max-height 方法做影片也是一個好方法,如果能夠確定大致高度的話,使用此方法是最簡單也是最快的方法,但是如果不能確定大致高度或整體高度經常變化的話,可以考慮其他方法,
2. grid 影片
grid 網格布局,是一種較新的布局,號稱是最強大的布局方案,grid 布局不是本文的介紹重點,并且較為復雜,如果感興趣的話,可以參考相關文章,如:
- 寫給自己看的display: grid布局教程
- 最強大的 CSS 布局 —— Grid 布局
grid 布局中可以使用 fr 單位,fr 單位是支持過度影片的(0fr=>1fr),將 grid 布局下的子元素,初始設定為0fr,在 :hover 狀態下設定為 1fr,就能夠實作不定高度影片效果,但是如果子元素有內容,在設定 0fr 的時候,會被其內容撐開,所以要給子元素添加 min-height: 0;
.container {
display: grid;
grid-template-rows: 0fr;
overflow: hidden;
transition: all .3s;
}
.container:hover {
grid-template-rows: 1fr;
}
.container .child {
min-height: 0;
}
如果想要實作帶有基礎高度的展開收起影片,我們可以設定 min-height: 100px;
.container .child {
min-height: 100px;
}
雖然此時實作了帶有基礎高度的影片效果,但是可以看到,如果我把 transition: all 3s; 的影片時間設定的較大,就可以看出來,雖然有基礎高度,但是整個影片的效果還是要實作 0fr 到 1fr 的影片效果,基礎高度部分不會有影片效果,這也算是一個小的缺點,如果影片時間較短并且基礎高度也不大的話,可以這樣使用,并不會有太大的影響效果,
但是 grid 布局有可能有兼容性的問題,grid-template-rows 影片的支持可能有兼容性問題

3. js 控制影片
寫這篇文章的原因是因為在看專案代碼的時候看見了 $(.xx).slideDown() 方法實作了元素的下滑影片,覺得很不錯,想學習一下怎么實作的,實作效果如下:
但是在看元素的時候卻只能看見下面的樣子,發現不是 css 實作的,是使用 js 不斷改變元素的高度來實作的:
我又去看了一下 ant-design 的 Menu 組件,通過觀察元素,發現其也是不斷改變高度來實作的(Ps: 我并沒有去看原始碼,如果有誤,多謝指正),
實作
首先要思考整個實作的思路
展開的時候,元素從無到有,我們應該首先獲取整個元素的實際高度使用 offsetHeight 來獲取,獲取到整體高度后就要計算每一次增加或者減少的高度,通過定時器不斷增加或減少元素的高度,直到到了最大高度或 0 后停止
展開
const element = document.getElementById('container');
let expandTimer = null;
let offsetHeight = 0;
// 獲取元素總高度
element.style.display = 'block';
let height = 0;
// 先將 display 設定為 block,獲取到的 offsetHeight 才是正確的高度,之后才能設定元素高度
offsetHeight = element.offsetHeight;
const stepHeight = offsetHeight / 30;
element.style.height = height + 'px';
expandTimer = setInterval(() => {
height += stepHeight;
if (height >= offsetHeight) {
clearInterval(expandTimer);
element.style = null;
return;
}
element.style.height = height + 'px';
}, 10);
收起
let collapseTimer = null;
offsetHeight = element.offsetHeight;
let height = offsetHeight;
const stepHeight = offsetHeight / 30;
element.style.height = height + 'px';
collapseTimer = setInterval(() => {
height -= stepHeight;
if (height <= 0) {
clearInterval(collapseTimer);
element.style = null;
return;
}
element.style.height = height + 'px';
}, 10);
現在能夠正確展開收起,但是我們在展開收起的時候也會有相反的操作,比如滑鼠進入元素展開離開收起,在展開的程序中滑鼠離開了,我們應該立刻就將元素收起,而不是等影片結束后在進行下一個影片,所以要將展開收起操作合并操作才可以
const element = document.getElementById('container');
let expandTimer = null;
let collapseTimer = null;
// 我認為在一次展開后,直到收起完成之前,這個元素的實際高度都不應該發生變化,但是可以在下一次展開時發生變化,所以在展開時會進行賦值,在收起完成時會將此值清空
let offsetHeight = 0;
let stepHeight = 0;
const handleClick = () => {
// 如果當前 expandTimer 值存在,就標識當前是正在展開或已經展開,接下來要進行的是收起操作
if (expandTimer) {
clearInterval(expandTimer);
expandTimer = null;
// 收起時的初始高度是元素的當前實際高度,即使是元素在展開影片程序中,也要從當前元素高度進行收起影片
let height = element.offsetHeight;
collapseTimer = setInterval(() => {
height -= stepHeight;
if (height <= 0) {
// 高度小于等于 0 代表影片完成,將資料進行重置
clearInterval(collapseTimer);
offsetHeight = 0;
// 要將元素的高度置為 null,不然會影響下一次展開時獲取正確的高度
element.style.height = null;
// display 設為 null,要將元素隱藏
element.style.display = 'none';
return;
}
element.style.height = height + 'px';
}, 10);
} else {
clearInterval(collapseTimer);
collapseTimer = null;
// 獲取元素總高度
element.style.display = 'block';
let height = 0;
如果當前沒有 offsetHeight 就要重新獲取
if (!offsetHeight) {
offsetHeight = element.offsetHeight;
// 每一次給元素添加或減少的高度,除以 30 是自己設定的,跟下面定時器的每次間隔時間一起控制整個高度影片的時長,也可以給函式添加第二個時間引數,可以自由控制影片時間
stepHeight = offsetHeight / 30;
} else {
// 如果有 offsetHeight 就代表正在進行收起影片,應該從收起影片的當前高度進行展開影片
height = element.offsetHeight;
}
element.style.height = height + 'px';
expandTimer = setInterval(() => {
height += stepHeight;
if (height >= offsetHeight) {
// 當前高度如果已經到了元素的實際高度,就要清除定時器
clearInterval(expandTimer);
// 將 expandTimer 設為 1 是因為當前是以 expandTimer 判斷是否正在或已經進行了展開影片,所以要在完成是設為 1,在收起影片的開始時會將值設為 null
expandTimer = 1;
element.style = null;
return;
}
element.style.height = height + 'px';
}, 10);
}
};
最終實作效果
4. 總結
上面的三種方式實作效果都是各有千秋 - max-height 方法實作是最簡單,也是效率最高的方式,但是也有影片時間不定的缺陷 - grid 方式實作比 max-height 稍微復雜一些,但是整體效果要比 max-height 更好,但是目前瀏覽器的支持方面可能有所不足,如果有低版本的兼容性要求的話,還是不能使用 - js 方式整體最復雜,但是卻沒有上面兩種方式的缺陷與問題,使用范圍也更廣泛,但是是 js 的實作方式,性能肯定是不如 css,雖然不如,但是由于整體操作也較為簡單,所以也不會有什么性能問題
幾種方法的取舍全看個人需求了,
如果有滑鼠進入展開,離開收起的操作,可以配合使用 onmouseover onmouseout 事件來監聽滑鼠的進入離開,
其他還有像是 transform: scale(0); 的實作也是可以,但是整體影片效果就是一個縮小的效果,而且元素還會有占位問題,如果沒什么要求也是可以使用的,
本文轉載于:
https://juejin.cn/post/7249536369474486329
如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/556251.html
標籤:其他
下一篇:返回列表

