現代CSS強大的令人難以置信。
這次我們來用 CSS 實作這樣一個功能:有多個寬度不同的标簽水準排列,當外層寬度不足時,會提示超出的數量,示範效果如下
如果讓我用 JavaScript來實作估計都有點折騰,畢竟寬度都是動态的,要監聽各部分的尺寸變化,包括标簽的位置和外層的寬度,總之不是一兩行代碼就能搞定的。
現如今,純CSS也能完美的實作這樣的效果,而且比 JS 實作更加簡單,一起來看看吧
一、CSS 實作思路
很多時候,CSS并沒有直接的實作方式,需要“繞”點彎路,将需求拆分成很多小點,然後逐一突破。
回到本文這裡,其實有幾個問題需要考慮:
- CSS 如何動态累計數字?
- CSS 如何知道哪些元素在容器之外?
- CSS 如何區分是否溢出(顯示數量)
關于第3點,其實在之前這篇文章中有詳細介紹,有興趣可以回顧一下,非常有意思
純 CSS 檢測文本是否溢出
那麼第一點,CSS有沒有什麼方式可以統計數量呢?
沒錯,相信很多小夥伴已經猜到了,就是利用 CSS計數器,示意如下
counter-reset: num var(--num);
counter-increment: num;
::after{
content: "+"counter(num);
}
關于計數器,我之前也在很多文章中有用到
你可能不需要 JS!CSS實作一個計時器
如何讓CSS計數器支援小數的動态變化?
還在使用定時器嗎?CSS 也能實作電子時鐘
動畫合成小技巧!CSS 實作動感的倒計時效果
自定義計數器小技巧!CSS 實作長按點贊累積動畫
然後是第二點,CSS有什麼方式可以知道元素是出去了還是在視野之内呢?
如果是用 JS來判斷的話,最穩妥的方式應該Intersection Observer(交叉觀察者),相信很多同學都用過,這個在圖檔懶加載非常有用
交叉觀察器 API - Web API 接口參考 | MDN (mozilla.org)[1]
那麼,CSS中有什麼類似的呢?沒錯,還是之前提到過的CSS滾動驅動動畫。
不過這裡用到的是視圖進度,也就是關注的是元素自身位置,元素進入到容器範圍之内就會觸發動畫,非常類似 JS中的Intersection Observer
tag{
animation: appear;
animation-timeline: view(inline);
animation-range: contain;
}
@keyframes appear{
to {
background-color: #9747FF;
}
}
這裡就不詳細介紹了,有興趣回顧之前這篇文章
CSS 滾動驅動動畫終于正式支援了~
好了,關鍵原理就這些了,我們需要做的就是想辦法将「計數器」和「元素進出容器範圍」關聯起來就行了,接着往下看
二、CSS 計數器
利用 CSS 計數器,我們可以很輕松的統計元素的數量。
先簡單布局一下文章開頭的例子,HTML如下所示
<div class="con">
<a class="tag">HTML</a>
<a class="tag">CSS</a>
<a class="tag">JavaScript</a>
<a class="tag">Flutter</a>
<a class="tag">Vue</a>
<a class="tag">React</a>
<a class="tag">Svelte</a>
</div>
<span class="num"></span><!--用來計數的标簽-->
然後美化一下
.con{
display: flex;
gap: 5px;
padding: 5px;
overflow: hidden;
}
.tag{
padding: .2em .5em;
background-color: #c49ef5;
color: #fff;
border-radius: 4px;
animation: appear;
}
.num::after{
content: "+0";
padding: .2em .5em;
background-color: #FFE8A3;
color: #191919;
border-radius: 4px;
}
這樣就得到了水準排列的 tag 布局(超出隐藏),後面跟一個數字
現在我們定義一個計數器,然後通過每個tag進行累計,有多個tag相當于多少次累加,最後在::after中顯示出來
.con{
counter-reset: num; /*計數器初始值,預設為0*/
}
.tag{
counter-increment: num; /*計數器增量值,預設為1*/
}
.num::after{
content: "+"counter(num);
}
預設計數器的起始值是 0,每次累計是1,是以這裡最後得到了7,這樣就能實時統計元素的數量了
當然,我們也可以自定義這些預設值,比如
.con{
counter-reset: num 10;
}
.tag{
counter-increment: num -1;
}
這種情況下,起始值是10,增量值是-1,是以最後得到了3,如下
那麼,該如何統計容器之外的标簽數量呢?
三、CSS 滾動驅動動畫
這裡我們要利用CSS 視圖進度時間軸,也就是觀察元素自身的位置,在進入到容器範圍之内執行動畫。
拿上面這個例子,我們給标簽添加一個動畫,讓标簽在進入到容器之内時變個顔色,實作如下
.tag{
animation: appear;
animation-timeline: view(inline);
}
@keyframes appear{
to {
background-color: #4d47ff;
}
}
由于是水準方向,是以這裡需要設定view(inline) ,效果如下
好像不是很明顯?這是因為動畫是在整個視區内過渡的,我們可以改變動畫時間線的區間animation-range,讓這個動畫在進出的一瞬間就變化
.tag{
animation: appear;
animation-timeline: view(inline);
animation-range: contain;
}
@keyframes appear{
from,to {
background-color: #4d47ff;
}
}
效果如下
那麼,這個效果和前面的計數器有什麼關聯呢?
從效果上來看,在視區内會執行一個動畫,如果在這個動畫中加入計數器的累加會怎麼樣呢?先去除原先的累加器
.tag{
/* counter-increment: num; */
}
@keyframes appear{
from,to {
background-color: #4d47ff;
counter-increment: num;
}
}
效果如下
可以看到計數器已經生效了,不過現在統計的在「可視區内」标簽的數量。而我們要統計的是「可視區外」的數量,好像反過來了,如何實作呢?
這裡有兩種思路。
第一種,用總數減去可視區内的數量,不就行了嗎?由于現在動畫是針對可視區内的,我們可以給計數器設定一個初始值,就是總量,然後動畫中給累加值設定為-1,就相當于減去目前數量了,實作如下
.con{
counter-reset: num 7;
}
@keyframes appear{
from,to {
background-color: #4d47ff;
counter-increment: num -1;
}
}
這樣就完美統計出了可視區外的數量了
還有一種方式,我覺得更加巧妙,需要反向思考。原理是重置目前的累加值,比如預設情況下,正常累加,進入可視範圍,把累加值設定為0,不就相當于可視區外的正常累加了嗎?
.tag{
counter-increment: num;
}
@keyframes appear{
from,to {
background-color: #4d47ff;
counter-increment: num 0;
}
}
同樣能達到相同的效果
現在基本已經達到我們需要的效果了
四、其他細節修正
首先是,在超出範圍時,需要在邊緣出添加一個半透明蒙層,這樣體驗效果會更好。
半透明蒙層很好實作,隻需要添加一個水準漸變的mask遮罩就行了
.con{
-webkit-mask: linear-gradient(to right, #fff calc(100% - 30px), transparent);
}
效果如下
那麼,如何在沒有超出時,不出現這個遮罩呢?
這個其實在之前的文章中有詳細介紹了,建議回顧一下
純 CSS 檢測文本是否溢出
利用CSS滾動驅動動畫,可以檢測容器是否可滾動,也就是是否超出。是以我們隻需要将遮罩放在滾動驅動動畫裡就行了,關鍵實作如下
.con{
animation: check;
animation-timeline: scroll(x self);
}
@keyframes check{
from,to {
-webkit-mask: linear-gradient(to right, #fff calc(100% - 30px), transparent);
}
}
效果如下
還有一個問題,希望在沒有超出的時候不顯示後面的數量。
關于這個,我本來是打算用樣式查詢來實作,但是遇到了一個問題,由于樣式查詢隻能比對到子元素,是以不得不将顯示數量的标簽放到.con容器内。但是這樣一來,mask遮罩就會出現問題,就像這樣
遮罩層連同數量标簽一起被裁剪了!
于是又想出了另一種方案,這個在以前的布局中其實用到的更多,那就是負margin。
實作很簡單,給标簽容器一個的負margin-right,這樣,右邊的數量就會被左邊的标簽蓋住,比如
.con{
margin-right: -20px;
}
效果如下
是不是已經被蓋住一部分了?我們繼續向左偏移
.con{
margin-right: -50px;
}
這樣就完全看不到右邊的數量了
那麼,如何在超出時顯示數量呢?這裡就展現出負margin的好處了,可以借助前一個元素來隐藏後一個元素,在這裡,我們直接在前面的動畫中還原margin就行了
.con{
animation: check;
animation-timeline: scroll(x self);
}
@keyframes check{
from,to {
-webkit-mask: linear-gradient(to right, #fff calc(100% - 30px), transparent);
margin-right: 0;
}
}
這樣就完美實作了文章開頭所示效果了
你也可以通路以下連結來體驗真實效果(Chrome115+)
- CSS auto tag num (juejin.cn)[2]
- CSS auto tag num (codepen.io)[3]
五、總結一下
以上就是本文的全部内容了,不知道你學會了嗎?下面總結一下實作重點:
- 很多時候,CSS并沒有直接的實作方式,需要“繞”點彎路
- CSS計數器可以實作數字累加
- CSS滾動驅動動畫可以檢測元素是否在可視範圍之内
- CSS滾動驅動動畫還能檢測元素是否内容超出
- 計數累加器可以放在滾動驅動動畫中,這樣在動畫觸發時也就相當于計數器生效了,這樣就能統計可視區的标簽數量了
- 樣式查詢隻能比對到子元素,但是用子元素會受到mask遮罩的影響
- 負margin可以借助前一個元素來隐藏後一個元素
[1]交叉觀察器 API - Web API 接口參考 | MDN (mozilla.org): https://developer.mozilla.org/zh-CN/docs/Web/API/Intersection_Observer_API
[2]CSS auto tag num (juejin.cn): https://code.juejin.cn/pen/7352565668073439241
[3]CSS auto tag num (codepen.io): https://codepen.io/xboxyan/pen/rNbYpZV
作者:XboxYan
來源-微信公衆号:前端偵探
出處:https://mp.weixin.qq.com/s/_m5UxLQQtDU1GxbLRnCUhQ