用純css模擬下雪的效果


?
1、前言
由于公司產(chǎn)品的活動(dòng),需要模擬類似下雪的效果。瀏覽器實(shí)現(xiàn)動(dòng)畫(huà)無(wú)非css3和canvas(還有g(shù)if),對(duì)比下css3和canvas的優(yōu)缺點(diǎn):
動(dòng)畫(huà)自由度:canvas勝;
復(fù)雜度:canvas勝;
兼容性:canvas勝;
性能:css3勝(requestAnimationFrame和硬件加速)。
由于對(duì)于性能有一定的要求,canvas對(duì)比css3會(huì)有更多的計(jì)算量導(dǎo)致性能可能不太好,所以選用css3模擬下雪效果(ps:能用css解決的問(wèn)題就不用JavaScript解決哈哈)。
2、原理
本文所采用的是css3的animation。為dom元素添加animation屬性就可以模擬動(dòng)畫(huà),例如w3school的例子:
animation .animation{width:100px;height:100px;background:red;position:relative;animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;}@keyframes mymove{from {left:0px;}to {left:200px;}}
當(dāng)然這誰(shuí)都會(huì),但有個(gè)問(wèn)題是,下雪并不是機(jī)械的下落,而是有快有慢、擺動(dòng)幅度、時(shí)間不定,這里的重點(diǎn)是需要構(gòu)造隨機(jī)性,理性分析下。
在瀏覽器中下雪的起始點(diǎn)隨機(jī);
下雪的速度隨機(jī);
下雪從開(kāi)始到落到地面的時(shí)間隨機(jī)(延時(shí)隨機(jī)和整個(gè)下雪過(guò)程的時(shí)間隨機(jī));
下雪過(guò)程中搖擺隨機(jī)。
我們可以在css中找到這幾點(diǎn)隨機(jī)性的對(duì)應(yīng)屬性:
起始點(diǎn):position的左右方位(動(dòng)畫(huà)我們一般采用定位,因?yàn)榭梢詼p少不必要的重排和重繪);
速度:animation-timing-function(提供了豐富的速度屬性);
時(shí)間:animation-duration和animation-delay;
搖擺:transform: translateX()(在水平方向上的位移)。
可能有人會(huì)問(wèn),這幾個(gè)屬性并不是隨機(jī)的呀,并不像Math.random隨機(jī)函數(shù)那樣。我們換個(gè)思考方向,本文所說(shuō)的隨機(jī)是隨機(jī)雪,并不是隨機(jī)雪的屬性。
每個(gè)雪的下落時(shí)間、下落速度、擺動(dòng)幅度是固定的,雪與雪之間的下落時(shí)間、下落速度、擺動(dòng)幅度是不同,那么就達(dá)到效果了。
意識(shí)到這點(diǎn),就剩下最后一個(gè)問(wèn)題,怎樣給每個(gè)雪不同的下落時(shí)間、下落速度、擺動(dòng)幅度呢?這里我們使用真正的隨機(jī)函數(shù)Math.random,利用data自定義屬性配合Math.random和css的屬性匹配規(guī)則,就可以確定動(dòng)畫(huà)效果了。
3、構(gòu)造雪形狀
3.1 一條線性漸變

.linear{width: 100px;height: 100px;background: linear-gradient(0, green 40%, red 40%, red 60%, green 60%);}
3.2 多條線性漸變畫(huà)雪花

.linear{width: 100px;height: 100px;border-radius: 50%;background-image: linear-gradient(0, rgba(255,255,255,0) 40%, #fff 40%, #fff 60%, rgba(255,255,255,0) 60%),linear-gradient(45deg, rgba(255,255,255,0) 43%, #fff 43%, #fff 57%, rgba(255,255,255,0) 57%),linear-gradient(90deg, rgba(255,255,255,0) 40%, #fff 40%, #fff 60%, rgba(255,255,255,0) 60%),linear-gradient(135deg, rgba(255,255,255,0) 43%, #fff 43%, #fff 57%, rgba(255,255,255,0) 57%);}
4、構(gòu)造下雪動(dòng)畫(huà)
我用sass寫(xiě)的css來(lái)構(gòu)造動(dòng)畫(huà),其實(shí)語(yǔ)言無(wú)所謂,重在原理。
4.1 起始點(diǎn)
@for $i from 1 through 100 {.animation[>'#{$i}'] {left: #{$i}vw;}}
這里運(yùn)用sass的循環(huán)函數(shù)來(lái)創(chuàng)造1到100的匹配class名為animation和屬性選擇器為[>
范圍1~100vw:注意單位是vw,我們想要雪花的起始點(diǎn)出現(xiàn)在水平方向的任意位置,而vw是把屏幕等分為100份,100vw就是滿屏;
為什么要把1~100都循環(huán)出來(lái):只有這樣才能匹配到范圍內(nèi)我們需要的值。
4.2 速度
$timing: (linear: linear,ease: ease,ease-in: ease-in,ease-out: ease-out,ease-in-out: ease-in-out);@each $key, $value in $timing {.animation[>'#{$key}'] {transition-timing-function: $value;}}
4.3 時(shí)間
時(shí)間包含動(dòng)畫(huà)運(yùn)動(dòng)時(shí)間和延時(shí)時(shí)間。
@for $i from 1 through 4 {.animation[>'#{$i}'] {animation-delay: #{$i}s;}}@for $i from 4 through 8 {.animation[>'#{$i}'] {animation-duration: #{$i}s;}}
4.4 搖擺
下雪由兩個(gè)動(dòng)畫(huà)組成,分別是下落fall和搖擺swing,下落的動(dòng)畫(huà)是固定的,只有搖擺需要隨機(jī),所以我自定義了屬性匹配規(guī)則,就可以隨機(jī)搖擺了。
@for $i from 1 through 4 {.animation[>'#{$i}'] {animation-name: fall, swing#{$i};}}@for $i from 1 through 4 {@keyframes swing#{$i}{25% {transform: translateX(-#{$i * 10}px);}50% {transform: translateX(#{$i * 10}px);}75%{transform: translateX(-#{$i * 10}px);}100%{transform: translateX(#{$i * 10}px);}}}
5、隨機(jī)性
這里用react來(lái)演示代碼,還是那句話只要理解了原理,語(yǔ)言其實(shí)并不太重要。
5.1 雪花元素
{Array(10).fill().map((v, i) => (key={i}className='animation span'>{this.position()}>{this.timing()}>{this.delay()}>{this.duration()}>{this.name()}/>))}
我們的雪花元素包含幾下幾個(gè)屬性值:
5.1 隨機(jī)范圍函數(shù)
random(min, max){return Math.floor(Math.random() * (max - min + 1) + min)}
5.2 屬性隨機(jī)賦值
position(){return this.random(1, 100)}delay(){return this.random(1, 4)}duration(){return this.random(4, 8)}name(){return this.random(1, 4)}timing(){return ['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out'][this.random(0, 4)]}

