CSS 實(shí)現(xiàn)3d輪播圖的一些思路,你學(xué)會(huì)了嗎?
圖片
說(shuō)難不難,說(shuō)簡(jiǎn)單也不簡(jiǎn)單,這是一個(gè)無(wú)限循環(huán)的輪播效果,并且個(gè)數(shù)是固定的,你會(huì)如何實(shí)現(xiàn)呢?
原效果是通過(guò)vue的transition組件實(shí)現(xiàn)的,感覺(jué)有些笨重,思考了一番,發(fā)現(xiàn)純 CSS也能實(shí)現(xiàn)這樣的效果,而且性能更好,實(shí)現(xiàn)也更簡(jiǎn)潔,一起來(lái)看看吧!
一、CSS 動(dòng)畫(huà)實(shí)現(xiàn)思路
首先來(lái)分析一下思路。看著是一個(gè)連貫的輪播效果,其實(shí)單獨(dú)看每一個(gè)子項(xiàng),都是完全相同的運(yùn)動(dòng)軌跡。
圖片
然后是這個(gè)動(dòng)畫(huà),其實(shí)就是6個(gè)關(guān)鍵幀,逐一去位移和縮放,如下:
圖片
然后給每個(gè)子項(xiàng)不同的“負(fù)延遲”,是不是就剛好錯(cuò)開(kāi),形成一個(gè)完整的輪播效果了呢?
無(wú)論CSS怎么實(shí)現(xiàn),動(dòng)畫(huà)原理就這些了,下面來(lái)看幾個(gè)實(shí)現(xiàn)方式
二、CSS 動(dòng)畫(huà)關(guān)鍵幀
首先簡(jiǎn)單布局一下,先用一個(gè)元素實(shí)現(xiàn)動(dòng)畫(huà);
<div class="item"></div>
加點(diǎn)修飾;
html,body{
font-family: -apple-system, "BlinkMacSystemFont", sans-serif;
margin: 0;
height: 100%;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
background: aliceblue;
counter-reset: num;
}
.item{
position: absolute;
display: grid;
place-content: center;
width: 100px;
height: 100px;
border-radius: 8px;
background-color: #3E65FF;
color: #fff;
font-size: 30px;
counter-increment: num;
}
這里的數(shù)字是用CSS計(jì)數(shù)器生成的,效果如下:
圖片
根據(jù)前面的關(guān)鍵幀,很容易用代碼實(shí)現(xiàn),就是。
.item{
animation: slide 12s infinite;
}
@keyframes slide {
0%,100% {
transform: translate(0%, 0%) scale(1);
}
16.67% {
transform: translate(120%, -30%) scale(0.9);
}
33.33% {
transform: translate(100%, -70%) scale(0.8);
}
50% {
transform: translate(0, -90%) scale(0.7);
}
66.67% {
transform: translate(-100%, -70%) scale(0.8);
}
83.33% {
transform: translate(-120%, -30%) scale(0.9);
}
}
這里的16.67%關(guān)鍵幀是將100%進(jìn)行6等分得到的,如下所示:
圖片
效果如下:
圖片
雖然有動(dòng)畫(huà)了,但是效果有點(diǎn)奇怪,每次改變位置的時(shí)候好像沒(méi)有停頓,顯得過(guò)渡有些緩慢,只是有減速和加速的過(guò)程,這是默認(rèn)的ease-in-out的淡入淡出緩沖效果。
那么,如何拉開(kāi)一定的間隔呢?也就是希望每次運(yùn)動(dòng)的快一點(diǎn),然后停留一會(huì)。其實(shí)也很簡(jiǎn)單,在之前的每個(gè)關(guān)鍵點(diǎn)前再添加一個(gè)相同的關(guān)鍵幀,比如在16.67%的前面一點(diǎn)6.67%,由于是相同的,所以這段時(shí)間內(nèi)是沒(méi)有動(dòng)畫(huà)的,也就相當(dāng)于停留了一會(huì)。
圖片
用代碼實(shí)現(xiàn)就是:
@keyframes slide {
0%,90%,100% {
transform: translate(0%, 0%) scale(1);
}
6.67%,
16.67% {
transform: translate(120%, -30%) scale(0.9);
}
23.33%,
33.33% {
transform: translate(100%, -70%) scale(0.8);
}
40%,
50% {
transform: translate(0, -90%) scale(0.7);
}
56.67%,
66.67% {
transform: translate(-100%, -70%) scale(0.8);
}
73.33%,
83.33% {
transform: translate(-120%, -30%) scale(0.9);
}
}
這樣是不是就好多了?
圖片
實(shí)現(xiàn)了一個(gè),多個(gè)子元素也就好辦了。
<div class="item" style="--i: 0"></div>
<div class="item" style="--i: 1"></div>
<div class="item" style="--i: 2"></div>
<div class="item" style="--i: 3"></div>
<div class="item" style="--i: 4"></div>
<div class="item" style="--i: 5"></div>
我們給每個(gè)子元素加個(gè)CSS變量,然后給每個(gè)動(dòng)畫(huà)加個(gè)“負(fù)延遲”,讓這個(gè)動(dòng)畫(huà)提前運(yùn)行到指定位置;
.item{
animation: slide 12s calc(-2s * var(--i)) infinite;
}
效果如下:
基本就實(shí)現(xiàn)這個(gè)這個(gè)輪播動(dòng)畫(huà),不過(guò)層級(jí)還有點(diǎn)問(wèn)題。
所以還需要再關(guān)鍵幀里加入層級(jí)變化;
@keyframes slide {
0%,90%,100% {
z-index: 4;
transform: translate(0%, 0%) scale(1);
}
6.67%,
16.67% {
z-index: 3;
transform: translate(120%, -30%) scale(0.9);
}
23.33%,
33.33% {
z-index: 2;
transform: translate(100%, -70%) scale(0.8);
}
40%,
50% {
z-index: 1;
transform: translate(0, -90%) scale(0.7);
}
56.67%,
66.67% {
z-index: 2;
transform: translate(-100%, -70%) scale(0.8);
}
73.33%,
83.33% {
z-index: 3;
transform: translate(-120%, -30%) scale(0.9);
}
}
這樣就完美了。
這種實(shí)現(xiàn)兼容性最好,只用到了 CSS 動(dòng)畫(huà),兼容市面所有瀏覽器,可以放心使用
- CSS 3d swiper (codepen.io)[1]
- CSS 3d swiper (juejin.cn)[2]
三、CSS @property
其實(shí)目前來(lái)說(shuō),用上面這種方式就足夠了,沒(méi)有任何問(wèn)題。
不過(guò),上面對(duì)于中間停頓的處理方式可能有些繁瑣,下面再介紹另一種思路,可能更符合常規(guī)。
先思考一下,如果是用 JS要如何實(shí)現(xiàn)?是不是可以直接每隔2秒改變位移和縮放,然后通過(guò)transition實(shí)現(xiàn)過(guò)渡效果?
首先,我們還是需要像之前一樣,定義一些關(guān)鍵幀,不過(guò)不是直接改變位移和縮放,而是改變一些 CSS變量。
@keyframes slide {
0%,100% {
--translate: 0,0;
--scale: 1;
--z: 4;
}
16.67% {
--translate: 120%,-30%;
--scale: 0.9;
--z: 3;
}
33.33% {
--translate: 100%,-70%;
--scale: 0.8;
--z: 2;
}
50% {
--translate: 0%,-90%;
--scale: 0.7;
--z: 1;
}
66.67% {
--translate: -100%,-70%;
--scale: 0.8;
--z: 2;
}
83.33% {
--translate: -120%,-30%;
--scale: 0.9;
--z: 3;
}
}
然后每個(gè)子項(xiàng)就需要用transfrom來(lái)應(yīng)用這些變量了;
.item{
transform: translate(var(--translate)) scale(var(--scale));
transition: .5s transform;
animation: slide 12s calc(-2s * var(--i)) infinite;
}
我們來(lái)看看效果:
好像并沒(méi)有過(guò)渡動(dòng)畫(huà)?這是因?yàn)閍nimation覆蓋了transition,所以需要分離開(kāi)來(lái),我們嵌套一層父級(jí)。
<div class="item-wrap" style="--i: 0">
<div class="item"></div>
</div>
<div class="item-wrap" style="--i: 1">
<div class="item"></div>
</div>
<div class="item-wrap" style="--i: 2">
<div class="item"></div>
</div>
<div class="item-wrap" style="--i: 3">
<div class="item"></div>
</div>
<div class="item-wrap" style="--i: 4">
<div class="item"></div>
</div>
<div class="item-wrap" style="--i: 5">
<div class="item"></div>
</div>
然后將動(dòng)畫(huà)寫(xiě)在父級(jí)上;
.item-wrap{
animation: slide 12s calc(-2s * var(--i)) infinite;
display: contents;
}
這樣就不影響了,效果如下:
不過(guò)還是有點(diǎn)怪怪的。這是因?yàn)閍nimation的默認(rèn)效果也是ease-in-out,所以有這種漸入漸出的效果。我們需要瞬間變化,不需要過(guò)渡效果,因?yàn)檫^(guò)渡效果可以由transition完成。
這里我們可以用steps來(lái)實(shí)現(xiàn),steps(1)表示直接切換,中間沒(méi)有任何過(guò)渡。
.item-wrap{
animation: slide 12s calc(-2s * var(--i)) steps(1) infinite;
}
這樣效果就和前面基本一致了。
你也可以訪問(wèn)以下鏈接來(lái)查看實(shí)際效果:
- CSS 3d swiper @property (codepen.io)[3]
- CSS 3d swiper @property (juejin.cn)[4]
雖然里面沒(méi)有提到CSS @property,但實(shí)際上是依賴這個(gè)特性的,因此兼容性稍差,需要Safari 16.4+,F(xiàn)irefox目前還不支持。
四、CSS 樣式查詢
拋開(kāi)兼容性,其實(shí)還有一種方式可以實(shí)現(xiàn),而且更容易理解,也更符合JS的思路。
我們要做的動(dòng)畫(huà)很簡(jiǎn)單,只需要改變一個(gè) CSS變量就行,如下:
@property --index {
syntax: "<number>";
initial-value: 0;
inherits: false;
}
@keyframes slide {
0% {
--index: 0;
}
100% {
--index: 6;
}
}
通過(guò)@property可以讓--index變量從0→6一次變化。
關(guān)于這個(gè)技巧,之前在多篇文章中都有提到。
你可能不需要 JS!CSS實(shí)現(xiàn)一個(gè)計(jì)時(shí)器。
如何讓CSS計(jì)數(shù)器支持小數(shù)的動(dòng)態(tài)變化?
還在使用定時(shí)器嗎?CSS 也能實(shí)現(xiàn)電子時(shí)鐘。
動(dòng)畫(huà)合成小技巧!CSS 實(shí)現(xiàn)動(dòng)感的倒計(jì)時(shí)效果。
自定義計(jì)數(shù)器小技巧!CSS 實(shí)現(xiàn)長(zhǎng)按點(diǎn)贊累積動(dòng)畫(huà)。
實(shí)現(xiàn)如下:
.item-wrap{
display: contents;
animation: slide 12s calc(-2s * var(--i)) steps(6) infinite;
}
這樣就實(shí)現(xiàn)了一個(gè)--index不斷變化的動(dòng)畫(huà)。
當(dāng)然僅僅只是這樣還不夠,我們需要根據(jù)這個(gè)變量來(lái)匹配具體的樣式,這就要用到CSS樣式查詢了,具體實(shí)現(xiàn)如下:
@container style(--index: 0) {
.item {
transform: translate(0, 0) scale(1);
z-index: 4;
}
}
@container style(--index: 1) {
.item {
transform: translate(120%, -30%) scale(0.9);
z-index: 3;
}
}
@container style(--index: 2) {
.item {
transform: translate(100%, -70%) scale(0.8);
z-index: 2;
}
}
@container style(--index: 3) {
.item {
transform: translate(0, -90%) scale(0.7);
z-index: 1;
}
}
@container style(--index: 4) {
.item {
transform: translate(-100%, -70%) scale(0.8);
z-index: 2;
}
}
@container style(--index: 5) {
.item {
transform: translate(-120%, -30%) scale(0.9);
z-index: 3;
}
}
這段應(yīng)該很好理解,比如@container style(--index: 5) 表示,當(dāng)查詢到--index為5的時(shí)候,下面的樣式就生效了,非常像通過(guò) JS 來(lái)改變類名一樣,這種方式也能實(shí)現(xiàn)類似的效果。
你也可以訪問(wèn)以下鏈接來(lái)查看實(shí)際效果:
- CSS 3d swiper @style (codepen.io)[5]
- CSS 3d swiper @style (juejin.cn)[6]
由于要用到樣式查詢,所以兼容性更差一點(diǎn),需要Chrome 111+,酌情使用。
五、總結(jié)一下
以上就是本文的全部?jī)?nèi)容了,共介紹了3種不同的實(shí)現(xiàn)思路,兼容性從高到低,大家可以自行選擇。
對(duì)了,還有一點(diǎn),有時(shí)候我們需要鼠標(biāo)hover時(shí)暫停動(dòng)畫(huà),這個(gè)就體現(xiàn)出CSS的優(yōu)勢(shì)了,直接用:hover實(shí)現(xiàn),類似這樣
.wrap:hover .item{
animation-play-state: paused;
}
下面總結(jié)一下實(shí)現(xiàn)要點(diǎn):
- 整體看著是一個(gè)連貫的輪播效果,其實(shí)單獨(dú)看每一個(gè)子項(xiàng),都是完全相同的運(yùn)動(dòng)軌跡。
- 給每個(gè)子項(xiàng)不同的“負(fù)延遲”,就能形成一個(gè)完整的輪播效果。
- 動(dòng)畫(huà)本質(zhì)上是6個(gè)關(guān)鍵幀,只是位移和縮放的變化。
- 直接使用關(guān)鍵幀雖然有動(dòng)畫(huà),但是效果有點(diǎn)奇怪,每次改變位置的時(shí)候好像沒(méi)有停頓,顯得過(guò)渡有些緩慢。
- 可以手動(dòng)在每個(gè)關(guān)鍵點(diǎn)前再添加一個(gè)相同的關(guān)鍵幀,手動(dòng)停頓一下。
- 給每個(gè)子元素加個(gè)CSS變量,通過(guò)這個(gè)變量可以計(jì)算每個(gè)動(dòng)畫(huà)的“負(fù)延遲”,讓這個(gè)動(dòng)畫(huà)提前運(yùn)行到指定。
- 我們還定義一些關(guān)鍵幀,不過(guò)不是直接改變位移和縮放,而是改變一些 CSS變量。
- 這樣可以實(shí)現(xiàn)每隔2秒改變位移和縮放,然后通過(guò)transition實(shí)現(xiàn)過(guò)渡效果。
- 這種思路依賴CSS @property,兼容性稍差,需要Safari 16.4+,F(xiàn)irefox目前還不支持。
- 還可以通過(guò)樣式查詢,自動(dòng)匹配每種變量的位移和縮放,更符合常規(guī)思路,需要Chrome 111+