怎么使用純 CSS 實(shí)現(xiàn)有趣的點(diǎn)贊動(dòng)畫?下面本篇文章就帶大家了解一下巧妙借助 transition實(shí)現(xiàn)點(diǎn)贊動(dòng)畫的方法,希望對(duì)大家有所幫助!
前端(vue)入門到精通課程:進(jìn)入學(xué)習(xí)
在各種短視頻界面上,我們經(jīng)常會(huì)看到類似這樣的點(diǎn)贊動(dòng)畫:
非常的有意思,有意思的交互會(huì)讓用戶更愿意進(jìn)行互動(dòng)。
那么,這么有趣的點(diǎn)贊動(dòng)畫,有沒有可能使用純 CSS 實(shí)現(xiàn)呢?那當(dāng)然是必須的,本文,就將巧妙的借助 transition
,僅僅使用 CSS 完成這么一個(gè)點(diǎn)贊動(dòng)畫。【推薦學(xué)習(xí):css視頻教程】
實(shí)現(xiàn)不同表情的不斷上升
如果使用純 CSS 實(shí)現(xiàn)這一整套動(dòng)畫的話。我們首先需要實(shí)現(xiàn)一段無限循環(huán)的,大量不同的表情不斷向上漂浮的動(dòng)畫。
像是這樣:
這個(gè)整體還是比較容易實(shí)現(xiàn)的,核心原理就是同一個(gè)動(dòng)畫,設(shè)置不同的 transition-duration
,transition-dalay
,和一定范圍內(nèi)的旋轉(zhuǎn)角度。
我們首先要實(shí)現(xiàn)多個(gè)表情,一個(gè) DOM 標(biāo)簽放入一個(gè)隨機(jī)的表情。
我們可以手動(dòng)一個(gè)一個(gè)的添加:
<ul class="g-wrap"> <li>?</li> <li>❤️</li> <li>?</li> // ... 隨機(jī)設(shè)置不同的表情符號(hào),共 50 個(gè) <li>...</li> </ul>
當(dāng)然,我個(gè)人覺得這樣太麻煩。我習(xí)慣利用 SASS 的循環(huán)函數(shù)及隨機(jī)函數(shù),利用偽元素的 content
去隨機(jī)生成不同表情。像是這樣:
<ul class="g-wrap"> <li></li> <li></li> <li></li> // ... 共50個(gè)空標(biāo)簽 </ul>
$expression: "?", "?", "❤️", "?", "?", "?", "?", "?", "??", "?", "?", "?", "?", "?"; .g-wrap { position: relative; width: 50px; height: 50px; } @for $i from 1 to 51 { li:nth-child(#{$i}) { position: absolute; top: 0; left: 0; width: 50px; height: 50px; &::before { content: nth($expression, random(length($expression))); position: absolute; font-size: 50px; } } }
這樣,我們就能得到 50 個(gè)疊加在一起的表情:
因?yàn)橥该鞫葹?1 的緣故,只能看到最上面的幾個(gè)表情,實(shí)際上這里疊加了 50 個(gè)不同的表情。
這里的核心就是 content: nth($expression, random(length($expression)))
,我們利用了 SASS 的 random 和 length 和 nth 等方法,隨機(jī)的將 $expression
列表中的表情,添加給了不同的 li 的 before 偽元素的 content 內(nèi)。
接下來,我們需要讓它們動(dòng)起來。
這個(gè)簡單,添加一個(gè)無限的 transform: translate()
動(dòng)畫即可:
@for $i from 1 to 51 { li:nth-child(#{$i}) { animation: move 3000ms infinite linear; } } @keyframes move { 100% { transform: translate(0, -250px); } }
效果如下:
OK,由于 50 個(gè)元素都疊加在一起,所以我們需要將動(dòng)畫區(qū)分開來,我們給它們添加隨機(jī)的動(dòng)畫時(shí)長,并且,賦予不同的負(fù) transition-delay
值:
@for $i from 1 to 51 { li:nth-child(#{$i}) { animation: move #{random() * 2500 + 1500}ms infinite #{random() * 4000 / -1000}s linear; } } @keyframes move { 100% { transform: translate(0, -250px); } }
效果如下:
效果已經(jīng)非常接近我們想要的了!這里有一點(diǎn)點(diǎn)的跳躍,需要理解 move #{random() * 2500 + 1500}ms infinite #{random() * 4000 / -1000}s linear
這里大段代碼:
-
#{random() * 2500 + 1500}ms
生成 1500ms ~ 4000ms 之間的隨機(jī)數(shù),表示動(dòng)畫的持續(xù)時(shí)長 -
#{random() * 4000 / -1000}s
生成 -4000ms ~ 0s 之間的隨機(jī)數(shù),表示負(fù)的動(dòng)畫延遲量,這樣做的目的是為了讓動(dòng)畫提前進(jìn)行
如果你對(duì)負(fù)的
transition-delay
的作用還不了解,可以看看我的這篇文章 — 深入淺出 CSS 動(dòng)畫
到這,還是不夠隨機(jī),我們?cè)偻ㄟ^隨機(jī)添加一個(gè)較小的旋轉(zhuǎn)角度,讓整體的效果更加的隨機(jī):
@for $i from 1 to 51 { li:nth-child(#{$i}) { transform: rotate(#{random() * 80 - 40}deg); animation: move #{random() * 2500 + 1500}ms infinite #{random() * 4000 / -1000}s linear; } } @keyframes move { 100% { transform: rotate(0) translate(0, -250px); } }
這里 transform: rotate(#{random() * 80 - 40}deg)
的作用就是隨機(jī)生成 -40deg ~ 40deg 的隨機(jī)數(shù),產(chǎn)生一個(gè)隨機(jī)的角度。
至此,我們就得到了這樣一個(gè)效果:
利用 transition 化腐朽為神奇
到這里。很多同學(xué)可能還不明白,明明是點(diǎn)贊一次產(chǎn)生一個(gè)表情,為什么需要一次生成這么多不斷運(yùn)動(dòng)的表情效果呢?
這是因?yàn)?,由?CSS 沒法直接正面做到點(diǎn)擊一次,生成一個(gè)表情,所以我們需要換一種思路實(shí)現(xiàn)。
如果這些表情一直都是在運(yùn)動(dòng)的,只不過不點(diǎn)擊的時(shí)候,它們的透明度都為 0,我們要做的,就是當(dāng)我們點(diǎn)擊的時(shí)候,讓它們從 opacity: 0
變到 opacity: 1
。
要實(shí)現(xiàn)這一點(diǎn),我們需要巧妙的用到 transition
。
我們以一個(gè)表情為例子:
-
默認(rèn)它的透明度為
opacity: 0.1
-
點(diǎn)擊的時(shí)候,它的透明度瞬間變成
opacity: 1
-
然后,通過
transition-delay
讓opacity: 1
的狀態(tài)保持一段時(shí)間后 -
逐漸再消失,變回
opacity: 0.1
看上去有億點(diǎn)點(diǎn)復(fù)雜,代碼會(huì)更容易理解:
li { opacity: .1; transition: 1.5s opacity 0.8s; } li:active { opacity: 1; transition: .1s opacity; }
效果如下:
一定要理解上面的代碼!巧妙地利用 transition
在正常狀態(tài)和 active
狀態(tài)下的變化,我們實(shí)現(xiàn)了這種巧妙的點(diǎn)擊效果。
如果我們把初始的 opacity: 0.1
改成 opacity: 0
呢?就會(huì)是這樣:
好,我們結(jié)合一下上面兩個(gè)動(dòng)畫:
-
我們將所有的表情,默認(rèn)的透明度改為
0.1
-
被點(diǎn)擊的時(shí)候,透明度變成
1
-
透明度在
1
維持一段時(shí)間,逐漸消失
代碼如下:
@for $i from 1 to 51{ li:nth-child(#{$i}) { position: absolute; top: 0; left: 0; width: 50px; height: 50px; transform: rotate(#{random() * 80 - 40}deg); animation: move #{random() * 2500 + 1500}ms infinite #{random() * 4000 / -1000}s linear; opacity: .1; transition: 1.5s opacity .8s; &::before { content: nth($expression, random(length($expression))); position: absolute; } } li:active { opacity: 1; transition: .1s opacity; } } @keyframes move { 100% { transform: rotate(0) translate(0, -250px); } }
效果如下:
嘿,是不是有那么點(diǎn)意思了!
好最后一步,我們通過一個(gè)點(diǎn)擊按鈕引導(dǎo)用戶點(diǎn)擊,并且給與一個(gè)點(diǎn)擊反饋,每次點(diǎn)擊的時(shí)候,點(diǎn)贊按鈕放大 1.1 倍,同時(shí),我們把默認(rèn)表情的透明度從 opacity: 0.1
徹底改為 opacity: 0
。
這樣,整個(gè)動(dòng)畫的完整的核心代碼:
<ul class="g-wrap"> <li></li> <li></li> <li></li> // ... 共50個(gè)空標(biāo)簽 </ul>
$expression: "?", "?", "❤️", "?", "?", "?", "?", "?", "??", "?", "?", "?", "?", "?"; .g-wrap { position: relative; width: 50px; height: 50px; &::before { content: "??"; position: absolute; width: 50px; height: 50px; transition: 0.1s; } &:active::before { transform: scale(1.1); } } @for $i from 1 to 51 { li:nth-child(#{$i}) { position: absolute; width: 50px; height: 50px; transform: rotate(#{random() * 80 - 40}deg); animation: move #{random() * 2500 + 1500}ms infinite #{random() * 4000 / -1000}s cubic-bezier(.46,.53,.51,.62); opacity: 0; transition: 1.5s opacity .8s; &::before { content: nth($expression, random(length($expression))); position: absolute; } } li:active { transition: .1s opacity; opacity: 1!important; } } @keyframes move { 100% { transform: rotate(0) translate(0, -250px); } }
這里,需要注意的是:
-
點(diǎn)贊的按鈕,通過了父元素
.g-wrap
的偽元素實(shí)現(xiàn),這樣的好處是,子元素 li 的:active
點(diǎn)擊事件,是可以冒泡傳給父元素的,這樣每次子元素被點(diǎn)擊,我們都可以放大一次點(diǎn)贊按鈕,用于實(shí)現(xiàn)點(diǎn)擊反饋; -
稍微修改一下緩動(dòng)函數(shù),讓整體效果更為均衡合理。
這樣,我們就得到了題圖一開始的效果,利用純 CSS 實(shí)現(xiàn)的點(diǎn)贊動(dòng)畫:
完整的代碼,你可以戳這里:CodePen Demo — Like Animation
一點(diǎn)瑕疵
當(dāng)然,這個(gè)方案是有一點(diǎn)點(diǎn)問題的。
-
1、就是如果當(dāng)點(diǎn)擊的速率過快,是無法實(shí)現(xiàn)一個(gè)點(diǎn)擊,產(chǎn)生一個(gè)表情的
這是由于 CSS 方案的本質(zhì)是通過點(diǎn)擊一個(gè)透明表情,讓它變成不透明。而點(diǎn)擊過快的話,會(huì)導(dǎo)致兩次或者多次點(diǎn)擊,點(diǎn)在了同一個(gè)元素上,這樣,就無法實(shí)現(xiàn)一個(gè)點(diǎn)擊,產(chǎn)生一個(gè)表情。所以上面代碼中修改緩動(dòng) cubic-bezier(.46,.53,.51,.62)
的目的也是在于,讓元素動(dòng)畫前期運(yùn)動(dòng)更快,這樣可以有利于適配更快的點(diǎn)擊速率。
-
2、不僅僅是點(diǎn)擊按鈕,點(diǎn)擊按鈕上方也能出現(xiàn)效果
這樣也很好理解,由于本質(zhì)是個(gè)障眼法,所以點(diǎn)擊按鈕上方,只要是元素運(yùn)動(dòng)路徑的地方,也是會(huì)有元素顯形的。這個(gè)硬要解決也可以,通過再疊加一層透明元素在按鈕上方,通過層級(jí)關(guān)系屏蔽掉點(diǎn)擊事件。
-
3、表情的隨機(jī)只是偽隨機(jī)
利用 SASS 隨機(jī)的方案在經(jīng)過編譯后是不會(huì)產(chǎn)生隨機(jī)效果的。所以,這里只能是偽隨機(jī),基于 DOM 的個(gè)數(shù),當(dāng) DOM 數(shù)越多,整體而言,隨機(jī)的效果越好?;旧?50 個(gè) DOM 是比較足夠的。
-
4、CSS 版本的點(diǎn)贊效果是單機(jī)版
無法多用戶聯(lián)動(dòng),可能是影響能不能實(shí)際使用最為關(guān)鍵的因素。
不過,總而言之,使用純 CSS 實(shí)現(xiàn)的方案,整體效果還是不錯(cuò)的。
(學(xué)習(xí)視頻分享:web前端)