這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
不定高度展開收起影片
最近在做需求的時候,遇見了元素高度展開收起的影片需求,一開始是想到了使用 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
標籤:其他
下一篇:返回列表