厲害了-純 CSS 實(shí)現(xiàn)波浪效果!
一直以來,使用純 CSS 實(shí)現(xiàn)波浪效果都是十分困難的。
因?yàn)閷?shí)現(xiàn)波浪的曲線需要借助貝塞爾曲線。

而使用純 CSS 的方式,實(shí)現(xiàn)貝塞爾曲線,額,暫時(shí)是沒有很好的方法。

當(dāng)然,借助其他力量(SVG、CANVAS),是可以很輕松的完成所謂的波浪效果的,先看看,非 CSS 方式實(shí)現(xiàn)的波浪效果。
使用 SVG 實(shí)現(xiàn)波浪效果
借助 SVG ,是很容易畫出三次貝塞爾曲線的。
看看效果:

代碼如下:
畫出三次貝塞爾曲線的核心在于
使用 canvas 實(shí)現(xiàn)波浪效果
使用 canvas 實(shí)現(xiàn)波浪效果的原理與 SVG 一樣,都是利用路徑繪制出三次貝塞爾曲線并賦予動(dòng)畫效果。

使用 canvas 的話,代碼如下:
$(function()?{
????let?canvas?=?$("canvas");
????let?ctx?=?canvas[0].getContext('2d');
????let?radians?=?(Math.PI?/?180)?*?180;
????let?startTime?=?Date.now();
????let?time?=?2000;
????let?clockwise?=?1;
????let?cp1x,?cp1y,?cp2x,?cp2y;
?????
????//?初始狀態(tài)
????//?ctx.bezierCurveTo(90,?28,?92,?179,?200,?100);
????//?末尾狀態(tài)
????//?ctx.bezierCurveTo(145,?100,?41,?100,?200,?100);
?????
????requestAnimationFrame(function?waveDraw()?{?
????????let?t?=?Math.min(1.0,?(Date.now()?-?startTime)?/?time);
???????????
????????if(clockwise)?{
????????????cp1x?=?90?+?(55?*?t);
????????????cp1y?=?28?+?(72?*?t);
????????????cp2x?=?92?-?(51?*?t);
????????????cp2y?=?179?-?(79?*?t);
????????}?else?{
????????????cp1x?=?145?-?(55?*?t);
????????????cp1y?=?100?-?(72?*?t);
????????????cp2x?=?41?+?(51?*?t);
????????????cp2y?=?100?+?(79?*?t);
????????}
?????????
????????ctx.clearRect(0,?0,?200,?200);
????????ctx.beginPath();
????????ctx.moveTo(0,?100);
????????//?繪制三次貝塞爾曲線
????????ctx.bezierCurveTo(cp1x,?cp1y,?cp2x,?cp2y,?200,?100);
????????//?繪制圓弧
????????ctx.arc(100,?100,?100,?0,?radians,?0);
????????ctx.fillStyle?=?"rgba(154,?205,?50,?.8)";
????????ctx.fill();
????????ctx.save();?
?????????
????????if(?t?==?1?)?{
????????????startTime?=?Date.now();
????????????clockwise?=?!clockwise;
????????}
?
????????requestAnimationFrame(waveDraw);
????});
})
主要是利用了動(dòng)態(tài)繪制 ctx.bezierCurveTo() 三次貝塞爾曲線實(shí)現(xiàn)波浪的運(yùn)動(dòng)效果,感興趣的可以自行研究。
純 CSS 實(shí)現(xiàn)波浪效果
好,接下來才是本文的重點(diǎn)!使用純 CSS 的方式,實(shí)現(xiàn)波浪的效果。
你 TM 在逗我?剛剛不是還說使用 CSS 無能為力嗎?

是,我們沒有辦法直接繪制出三次貝塞爾曲線,但是我們可以利用一些討巧的方法,模擬達(dá)到波浪運(yùn)動(dòng)時(shí)的效果,姑且把下面這種方法看作一種奇技淫巧。
原理
原理十分簡(jiǎn)單,我們都知道,一個(gè)正方形,給它添加 border-radius: 50%,將會(huì)得到一個(gè)圓形。

border-radius:用來設(shè)置邊框圓角,當(dāng)使用一個(gè)半徑時(shí)確定一個(gè)圓形。
好的,如果 border-radius 沒到 50%,但是接近 50% ,我們會(huì)得到一個(gè)這樣的圖形:

注意邊角,整個(gè)圖形給人的感覺是有點(diǎn)圓,卻不是很圓。額,這不是廢話嗎

好的,那整這么個(gè)圖形又有什么用?還能變出波浪來不成?
沒錯(cuò)!就是這么神奇。:) 我們讓上面這個(gè)圖形滾動(dòng)起來(rotate) ,看看效果:

可能很多人看到這里還沒懂旋轉(zhuǎn)起來的意圖,仔細(xì)盯著一邊看,是會(huì)有類似波浪的起伏效果的。
而我們的目的,就是要借助這個(gè)動(dòng)態(tài)變換的起伏動(dòng)畫,模擬制造出類似波浪的效果。
實(shí)現(xiàn)
當(dāng)然,這里看到是全景實(shí)現(xiàn)圖,所以感覺并不明顯,OK,讓我們用一個(gè)個(gè)例子看看具體實(shí)現(xiàn)起來能達(dá)到什么樣的效果。
我們利用上面原理可以做到的一種波浪運(yùn)動(dòng)背景效果圖:

CodePen Demo -- Pure CSS Wave[1]
后面漂浮的波浪效果,其實(shí)就是利用了上面的 border-radius: 45% 的橢圓形,只是放大了很多倍,視野之外的圖形都 overflow: hidden,只留下了一條邊的視野,并且增加了一些相應(yīng)的 transform 變換。
注意,這里背景是藍(lán)色靜止的,運(yùn)動(dòng)是白色的橢圓形。
代碼也很簡(jiǎn)單,SCSS 代碼如下:
body?{
????position:?relative;
????align-items:?center;
????min-height:?100vh;
????background-color:?rgb(118,?218,?255);
????overflow:?hidden;
?
????&:before,?&:after?{
????????content:?"";
????????position:?absolute;
????????left:?50%;
????????min-width:?300vw;
????????min-height:?300vw;
????????background-color:?#fff;
????????animation-name:?rotate;
????????animation-iteration-count:?infinite;
????????animation-timing-function:?linear;
????}
?
????&:before?{
????????bottom:?15vh;
????????border-radius:?45%;
????????animation-duration:?10s;
????}
?
????&:after?{
????????bottom:?12vh;
????????opacity:?.5;
????????border-radius:?47%;
????????animation-duration:?10s;
????}
}
?
@keyframes?rotate?{
????0%?{
????????transform:?translate(-50%,?0)?rotateZ(0deg);
????}
????50%?{
????????transform:?translate(-50%,?-2%)?rotateZ(180deg);
????}
????100%?{
????????transform:?translate(-50%,?0%)?rotateZ(360deg);
????}
}
為了方便寫 DEMO,用到的長(zhǎng)度單位是 VW 與 VH,不太了解這兩個(gè)單位的可以戳這里:vh、vw、vmin、vmax 知多少[2]
可能有部分同學(xué),還存在疑問,OK,那我們把上面的效果縮小 10 倍,將視野之外的動(dòng)畫也補(bǔ)齊,那么其實(shí)生成波浪的原理是這樣的:

圖中的虛線框就是我們實(shí)際的視野范圍。
值得探討的點(diǎn)
值得注意的是,要看到,這里我們生成波浪,并不是利用旋轉(zhuǎn)的橢圓本身,而是利用它去切割背景,產(chǎn)生波浪的效果。那為什么不直接使用旋轉(zhuǎn)的橢圓本身模擬波浪效果呢?因?yàn)橹虚g高,兩邊低的效果不符合物理學(xué)原理,看上去十分別扭;

可以點(diǎn)進(jìn)去看看下面這個(gè)例子:
CodePen Demo -- pure css wave[3]
使用純 CSS 實(shí)現(xiàn)波浪進(jìn)度圖
好,既然掌握了這種方法,下面我們就使用純 CSS 實(shí)現(xiàn)上面最開始使用 SVG 或者 CANVAS 才能實(shí)現(xiàn)的波浪進(jìn)度圖。
HTML 結(jié)構(gòu)如下:
"container">
????"wave">
.wave?{
????position:?relative;
????width:?200px;
????height:?200px;
????background-color:?rgb(118,?218,?255);
????border-radius:?50%;
??
????&::before,
????&::after{
????????content:?"";
????????position:?absolute;
????????width:?400px;
????????height:?400px;
????????top:?0;
????????left:?50%;
????????background-color:?rgba(255,?255,?255,?.4);
????????border-radius:?45%;
????????transform:?translate(-50%,?-70%)?rotate(0);
????????animation:?rotate?6s?linear?infinite;
????????z-index:?10;
????}
?????
????&::after?{
????????border-radius:?47%;
????????background-color:?rgba(255,?255,?255,?.9);
????????transform:?translate(-50%,?-70%)?rotate(0);
????????animation:?rotate?10s?linear?-5s?infinite;
????????z-index:?20;
????}
}
?
@keyframes?rotate?{
????50%?{
????????transform:?translate(-50%,?-73%)?rotate(180deg);
????}?100%?{
????????transform:?translate(-50%,?-70%)?rotate(360deg);
????}
}
效果圖:

CodePen Demo -- Pure Css Wave Loading[4]
雖然效果差了一點(diǎn)點(diǎn),但是相較于要使用學(xué)習(xí)成本更高的 SVG 或者 CANVAS,這種純 CSS 方法無疑可使用的場(chǎng)景更多,學(xué)習(xí)成本更低!
純 CSS 的充電效果
還能實(shí)現(xiàn)類似這樣的充電效果:

一些小技巧
單純的讓一個(gè) border-radius 接近 50 的橢圓形旋轉(zhuǎn),動(dòng)畫效果可能不是那么好,我們可以適當(dāng)?shù)奶砑右恍┢渌儞Q因素,讓動(dòng)畫效果看上去更真實(shí):
在動(dòng)畫過程中,動(dòng)態(tài)的改變 border-radius的值;在動(dòng)畫過程中,利用 transform對(duì)旋轉(zhuǎn)橢圓進(jìn)行輕微的位移、變形;上面也演示到了,多個(gè)橢圓同時(shí)轉(zhuǎn)動(dòng),賦予不同時(shí)長(zhǎng)的動(dòng)畫,并且添加輕微的透明度,讓整個(gè)效果更加逼真。
最后
好了,本文到此結(jié)束,希望對(duì)你有幫助 :)
如果還有什么疑問或者建議,可以多多交流,原創(chuàng)文章,文筆有限,才疏學(xué)淺,文中若有不正之處,萬望告知。
參考資料
CodePen Demo -- Pure CSS Wave: https://codepen.io/Chokcoco/pen/awxYWZ
[2]vh、vw、vmin、vmax 知多少: https://github.com/chokcoco/iCSS/issues/15
[3]CodePen Demo -- pure css wave: https://codepen.io/Chokcoco/pen/xreWZV
[4]CodePen Demo -- Pure Css Wave Loading: https://codepen.io/Chokcoco/pen/EXJrdB
