希望使用 CSS 實現(xiàn)如下所示的布局效果:

正常而言,我們的 HTML 結(jié)構(gòu)大致是如下所示:
<div class="g-container">
<div class="g-nav">
<ul>
<li>Tab 1</li>
<li>Tab 2</li>
<li>Tab 3</li>
<li>Tab 4</li>
</ul>
</div>
<div class="g-main">
<ul class="g-content">
<li>...</li>
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
</div>
</div>
對于 Hover 導(dǎo)航 Tab 時候的內(nèi)容切換,暫且不談。本文,我們核心想探討的是兩個點:
一是對于如下所示的不規(guī)則布局,應(yīng)該如何實現(xiàn):

并且,這里我們可能還需要給它加上陰影效果:

如何配合 Hover 動作,實現(xiàn)整個切換效果
帶著這兩個問題,我們一起來嘗試慢慢把這個效果實現(xiàn)。
借助偽元素實現(xiàn)不規(guī)則按鈕
首先,我們需要實現(xiàn)這個效果:

這個,其實在很多篇文章都有提及過:
想一想,這里其實就是豎向的 Chrome 分 Tab 的效果:
像是這樣:

我們對這個按鈕形狀拆解一下,這里其實是 3 塊的疊加:

只需要想清楚如何實現(xiàn)兩側(cè)的弧形三角即可。這里還是借助了漸變 -- 徑向漸變,其實他是這樣,如下圖所示,我們只需要把黑色部分替換為透明即可,使用兩個偽元素即可:

代碼如下:
<div class="outside-circle"></div>
.outside-circle {
position: relative;
background: #e91e63;
border-radius: 10px 10px 0 0;
&::before {
content: "";
position: absolute;
width: 20px;
height: 20px;
left: -20px;
bottom: 0;
background: #000;
background:radial-gradient(circle at 0 0, transparent 20px, #e91e63 21px);
}
&::after {
content: "";
position: absolute;
width: 20px;
height: 20px;
right: -20px;
bottom: 0;
background: #000;
background:radial-gradient(circle at 100% 0, transparent 20px, #e91e63 21px);
}
}
即可得到:

我們照葫蘆畫瓢,即可非常輕松的實現(xiàn)豎向的相同的效果,示意圖如下:

利用 drop-shadow 實現(xiàn)按鈕陰影
好,接下來,我們需要給按鈕添加上陰影效果,像是這樣:

因為使用了兩個偽元素,當(dāng)前單個按鈕在 Hover 狀態(tài)下的大致代碼如下:
li {
position: relative;
width: 160px;
height: 36px;
border-radius: 10px 0 0 10px;
background: #ddd;
&::before,
&::after {
content: "";
position: absolute;
right: 0;
border-radius: unset;
}
&::before {
width: 20px;
height: 20px;
top: -20px;
background: radial-gradient(circle at 0 0, transparent, transparent 19.5px, #ddd 20px, #ddd);
}
&::after {
width: 20px;
height: 20px;
bottom: -20px;
background: radial-gradient(circle at 0 100%, transparent, transparent 19.5px, #ddd 20px, #ddd);
}
}
如果使用 box-shadow
肯定是不行的,整個效果就會露餡:
嘗試給按鈕添加一個 box-shadow: 0 0 5px 0 #333
:

彎曲的連接處,明顯沒有陰影效果,怎么解決呢?
嗯哼,老讀者一定也知道,這里我們需要對整個可見部分添加陰影,需要使用 filter:drop-shadow()
。
drop-shadow()
濾鏡的作用用于創(chuàng)建一個符合元素(圖像)本身形狀(alpha 通道)的陰影。其中,最為常見的技巧,就是利用它生成不規(guī)則圖形的陰影。
因此,我們把上述的 box-shadow
替換成:filter: drop-shadow(0 0 5px #ddd)
:

這樣,我們就實現(xiàn)了基于單個不規(guī)則按鈕的陰影效果。
但是,顯然事情還沒有結(jié)束。
修改布局結(jié)構(gòu),再借助利用 drop-shadow 實現(xiàn)統(tǒng)一陰影
記得我們上面提到過的 HTML 的布局嗎?正常而言,右側(cè)的主體內(nèi)容和左側(cè)的導(dǎo)航,結(jié)構(gòu)是分離的:
<div class="g-container">
<div class="g-nav">
<ul>
<li>Tab 1</li>
// ...
</ul>
</div>
<div class="g-main">
<ul class="g-content">
<li>...</li>
// ...
</ul>
</div>
</div>
因此,這里最為麻煩的地方在于,左側(cè)按鈕的陰影,需要和右側(cè)的主體內(nèi)容連在一起!,所以當(dāng)我們給右側(cè)的 .g-main
也添加上相同的 filter:drop-shadow()
時,整個效果會變得非常奇怪:
// 當(dāng)前被 Hover 的 li
.g-nav li {
filter: drop-shadow(0 0 5px #ddd)
}
// 右側(cè)的主體
.g-main {
filter: drop-shadow(0 0 5px #ddd)
}
無論層級誰在上,整體陰影的展示都會瑕疵:

所以,如果想要實現(xiàn)整個元素的陰影是一整個的整體的效果,我們就不得不另辟蹊徑。
這里,我們的思路如下:
可以嘗試在 .g-main
中,添加一組與 .g-nav
相同的結(jié)構(gòu),負(fù)責(zé)樣式層面的展示
把新增的結(jié)構(gòu),利用絕對定位,讓其與實際的導(dǎo)航位置重疊
在原本的 .g-nav
中,通過 :has()
偽類,傳遞實時的 Hover 狀態(tài)
基于此,我們需要改造一下我們的結(jié)構(gòu):
<div class="g-container">
<div class="g-nav">
<ul>
<li>Tab 1</li>
<li>Tab 2</li>
<li>Tab 3</li>
<li>Tab 4</li>
</ul>
</div>
<div class="g-main">
<ul class="g-status">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<ul class="g-content">
<li>...</li>
// ...
</ul>
</div>
</div>
仔細(xì)看上面的結(jié)構(gòu),我們多了一組 .g-stauts
結(jié)構(gòu),放置在了 .g-main
之下。其 li 個數(shù)與實際的導(dǎo)航 .g-nav
保持一致,并且高寬大小都是一模一樣的。
并且,可以利用絕對定位,讓其完全疊加在 .g-nav
的位置上。
然后,我們把上述類 Chrome Tab 樣式的不規(guī)則按鈕結(jié)構(gòu)的 CSS 代碼結(jié)構(gòu),都賦給 .g-status
下的 li。
此時,由于不規(guī)則按鈕結(jié)構(gòu)和右側(cè)的主體內(nèi)容結(jié)構(gòu),其實是在一個父 div 之下,所以,我們只需要給 .g-main
元素添加 filter: drop-shadow()
,就可以實現(xiàn)一整個整體的陰影效果:

最后,我們利用 :has()
偽類,傳遞實時的 Hover 狀態(tài),把內(nèi)外的結(jié)構(gòu)連接起來。
最為核心的代碼如下:
.g-main {
background: #ddd;
filter: drop-shadow(0 0 3px #999);
}
.g-status {
position:absolute;
left: -160px;
top: 0;
width: 160px;
li {
width: 160px;
height: 36px;
position: relative;
background: #ddd;
opacity:0;
&::before,
&::after {
content: "";
position: absolute;
right: 0;
border-radius: unset;
}
&::before {
width: 20px;
height: 20px;
top: -20px;
background: radial-gradient(circle at 0 0, transparent, transparent 19.5px, #ddd 20px, #ddd);
}
&::after {
width: 20px;
height: 20px;
bottom: -20px;
background: radial-gradient(circle at 0 100%, transparent, transparent 19.5px, #ddd 20px, #ddd);
}
}
}
.g-status li {
opacity: 0;
}
.g-nav:has(li:nth-child(1):hover) + .g-main {
.g-status li:nth-child(1) {
opacity: 1;
}
}
.g-nav:has(li:nth-child(2):hover) + .g-main {
.g-status li:nth-child(2) {
opacity: 1;
}
}
.g-nav:has(li:nth-child(3):hover) + .g-main {
.g-status li:nth-child(3) {
opacity: 1;
}
}
.g-nav:has(li:nth-child(4):hover) + .g-main {
.g-status li:nth-child(4) {
opacity: 1;
}
}
什么意思呢?解釋一下:
事先把每一個 Tab 被 Hover 時的樣式,都寫在了 .g-stauts
中,并且再提醒一下,這個結(jié)構(gòu)是在 .g-main
之下的。然后,默認(rèn)設(shè)置隱藏即可;
實際觸發(fā) Hover 動畫效果,是正常的 .g-nav
下的一個一個的 li 結(jié)構(gòu);
當(dāng) .g-nav
下的一個一個的 li 被 Hover 時,我們通過 :has()
偽類,能夠拿到此事件,并且根據(jù)當(dāng)前是第幾個元素被 hover,對應(yīng)的控制實際在 .g-main
下的結(jié)構(gòu)進(jìn)行樣式的展示;
不太了解的 :has()
偽類的小伙伴,可以先讀一讀這篇文章 -- 淺談邏輯選擇器 is、where、not、has,此偽類的誕生,填補了在之前 CSS 選擇器中,沒有父選擇器的空缺。讓我們能夠在父元素節(jié)點上,根據(jù)子元素的狀態(tài)變化,做出樣式的調(diào)整。
這樣,我們就最終實現(xiàn)了我們文章一開始的效果:

文章可能有部分內(nèi)容沒有闡述的很清晰,完整的代碼其實行數(shù)非常之少,對文章內(nèi)容還不太理解的建議戳進(jìn) DEMO 中看看。
完整的 DEMO 效果:CodePen Demo -- Tab Hover Effect
有小伙伴會有疑問,為什么不直接在 .g-nav
導(dǎo)航結(jié)構(gòu)和 .g-main
結(jié)構(gòu)的父節(jié)點直接添加 drop-shadow()
,不是也可以實現(xiàn)一樣的效果嗎?
是的,對于本文貼出的代碼效果,是可以實現(xiàn)一樣的效果。但是,實際業(yè)務(wù)中,.g-nav
會更復(fù)雜,它們的共同父元素下也可能還有其他元素,實際情況遠(yuǎn)比本文貼出來的結(jié)構(gòu)復(fù)雜,因此借助多一層虛擬 ul,實際上是更好的解法。
最后
好了,本文到此結(jié)束,希望本文對你有所幫助 😃
轉(zhuǎn)自https://www.cnblogs.com/coco1s/p/18142894 作者ChokCoco
該文章在 2024/6/18 14:58:42 編輯過