簡單的下雨效果2.0版
前言
筆者上一篇釋出的文章有趣的css—簡單的下雨效果中有位老哥給我提了一個很棒的建議,大致意思是波紋應該產生於雨滴的消失處。
這是按照老哥的建議完善後的效果圖:
由於我製作GIF圖片的工具最多隻支援製作33FPS的GIF圖,所以看起來可能有一點點卡頓,實際的效果比圖片還是要好一些的,點選這裡可以線上檢視2.0版的效果。
思路
製作背景
首先給body中新增一個id為rain的div,並通過背景顏色線性漸變得到天空-地平線-海面的效果。
<!DOCTYPE html>
<html>
<head>
<meta name="charset" content="utf-8"/>
<title>簡單的下雨效果2.0</title>
</head>
<body>
<div id="rain"></div>
</body>
</html>
#rain {
position: relative;
height: 100%;
background: linear-gradient(#333,#999 ,#1f4794);
background-repeat: no-repeat;
background-size: 100% 100%;
}
製作雨滴
通過設定背景顏色徑向漸變得到圓形的水滴,再將其沿Y軸進行旋轉得到橢圓形的水滴,最後給其新增水滴下落的動畫效果。
.raindrop {
display: inline-block;
position: absolute;
top: 0;
left: 150px;
width: 5px;
height: 5px;
background: radial-gradient(#8fd4fc, #52b1f2, #0599fc);
border-radius: 5000px;
transform: rotateY(45deg);
animation: raindrop .8s;
}
@keyframes raindrop {
0% {top:5%;}
10% {top:10%;}
20% {top:20%;}
30% {top:30%;}
40% {top:40%;}
50% {top:50%;}
60% {top:60%;}
70% {top:70%;}
80% {top:80%;}
90% {top:90%;}
100% {top:95%;}
}
製作波紋效果
通過背景透明和圓形邊框得到圓形的環,再將其沿X軸進行旋轉得到橢圓形的環,最後給其新增環逐漸擴大的動畫效果。
.ripple {
display: inline-block;
position: absolute;
top: 60vh;
left: 50vh;
border: 2px solid #8fd4fc;
border-radius: 5000px;
background: rgba(0, 0, 0, 0);
transform: rotateX(72deg);
animation: ripple .6s;
}
@keyframes ripple {
0% {
width: 2px;
height: 2px;
}
10% {
width: 4px;
height: 4px;
}
20% {
width: 6px;
height: 6px;
}
30% {
width: 8px;
height: 8px;
}
40% {
width: 10px;
height: 10px;
}
50% {
width: 12px;
height: 12px;
}
60% {
width: 14px;
height: 14px;
}
70% {
width: 16px;
height: 16px;
}
80% {
width: 18px;
height: 18px;
}
90% {
width: 20px;
height: 20px;
}
100% {
width: 22px;
height: 22px;
}
}
在雨滴被移除的位置新增波紋
通過計算移除雨滴的隨機時間得到雨滴消失時距離頂部的距離。
let clientWidth;
let clientHeight;
window.onload = function onload(){
let rain = document.getElementById('rain');
clientWidth = document.body.clientWidth;
clientHeight = document.body.clientHeight;
function dorpRain(){
setTimeout(() => {
if(typeof clientWidth !== 'undefined' && null !== clientWidth){
let el = document.createElement('div');
el.setAttribute('class', 'raindrop');
let left = parseInt(Math.random() * clientWidth, 10) + 'px';
el.style.left = left;
rain.appendChild(el);
let time = parseInt(Math.random() * 350, 10);
setTimeout(() => {
rain.removeChild(el);
let newEl = document.createElement('div');
newEl.setAttribute('class', 'ripple');
newEl.style.left = left;
newEl.style.top = parseInt(clientHeight / 100 * 50 + (time / 350 * (clientHeight / 100 * 50)), 10) + 'px';
rain.appendChild(newEl);
setTimeout(() => {
rain.removeChild(newEl);
}, 600)
}, 400 + time, 10)
}
}, parseInt(10 + Math.random() * 10, 10))
}
dorpRain();
}
使波紋以水滴消失位置為圓心擴散
其實到這一步還是有個問題,就是波紋是向右下方擴散的,下面是筆者將動畫時間增大10倍,以及將波紋動畫中的屬性的寬高增大10倍後的效果:
產生此效果的原因是此時只能達到雨滴與波紋兩個屬性左上角的點重合,如果要使波紋以水滴消失位置為圓心擴散,需要讓兩個元素的中心點重合。
修改程式碼,先去掉rotate(原因後面再說),再使用translate(-50%, -50%)將元素向左上方移動,使兩個元素的中心點等於雨滴被移除時的位置。
.raindrop {
/* 將transform: rotateY(45deg)改為transform: translate(-50%, -50%),其餘不變*/
transform: translate(-50%, -50%);
/* ...... 其餘的css不要刪除*/
}
.ripple {
/* 將transform: rotateX(75deg)改為transform: translate(-50%, -50%),其餘不變*/
transform: translate(-50%, -50%);
/* ...... 其餘的css不要刪除*/
}
效果圖:
此時已經達到了波紋以水滴消失位置為圓心擴散,但要是加入了rotate之後,效果就完全不一樣了:
.raindrop {
/* 將transform: translate(-50%, -50%)改為transform: rotateY(45deg) translate(-50%, -50%),其餘不變*/
transform: rotateY(45deg) translate(-50%, -50%);
/* ...... 其餘的css不要刪除*/
}
.ripple {
/* 將transform: translate(-50%, -50%)改為transform: rotateX(75deg) translate(-50%, -50%),其餘不變*/
transform: rotateX(75deg) translate(-50%, -50%);
/* ...... 其餘的css不要刪除*/
}
效果圖:
可以看出此時的波紋是向下方擴散,這是為什麼呢?
讓我們先看一段很簡單的程式碼:
<!DOCTYPE html>
<html>
<head>
<meta name="charset" content="utf-8"/>
<title>測試</title>
</head>
<style type="text/css">
* {
padding: 0;
margin: 0;
}
div {
width: 100px;
height: 100px;
background-color: #fe0000;
margin-top: 20px;
}
</style>
<body>
<div></div>
<div style="transform: rotateX(75deg);"></div>
</body>
</html>
效果圖:
rotateX會改變元素的高度,但其並不會改變元素在rotateX前佔據的位置的大小!!!所以當我們使用同時使用 rotate 和translate(-50%, -50%)時並達到預期的效果。
就比如上面的資料,元素的寬高皆為100px,將其rotateX(75deg)後,寬度不變,translateX(-50%)會將元素移動到Y軸的中心線上;但此時元素的高度已經變為25.88px,translateY(-50%)只能將其向上移動12.94px,實際上是需要向上移動50px才能達到X軸的中心線上。
就比如下圖(嘗試將看待元素的視角從XY轉移到YZ)
A、B、C、D四條線,B的長度與A一致,AB之間的角度為45°,AD與CD之間皆為直角,那麼根據正弦定理,C的長度等於B的長度的sin45°,也就是A的長度的sin45°。
元素rotateX(75deg)後,height由100px變為25.88,也就是:
100 * sin(90° - 75°)
= 100 * sin(15°)
= 100 * 0.25881904510252
≈ 100 * 0.2588
≈ 25.88
所以rotateY(45deg)
sin(45°)
= 0.70710678118655
≈ 0.71
要使雨滴達到rotateY(45deg)前 translateX(-50%)的效果,需要 translateX(-50% / 0.71) 約等於 translateX(-70%),也就是:
.raindrop {
/* 將transform: rotateY(45deg) translate(-50%, -50%)改為transform: rotateY(45deg) translate(-70%, -50%),其餘不變*/
transform: rotateY(45deg) translate(-70%, -50%);
/* ...... 其餘的css不要刪除*/
}
rotateX(75deg)
sin(90° - 75°)
= sin(15°)
= 0.25881904510252
≈ 0.26
同理,要使波紋達到rotateX(75deg)前translateY(-50%)的效果,需要 translateY(-50% / 0.26) 約等於 translateY(-192%) ,也就是:
.ripple {
/* 將transform: rotateX(75deg) translate(-50%, -50%)改為transform: rotateX(75deg) translate(-50%, -192%),其餘不變*/
transform: rotateX(75deg) translate(-50%, -192%)
/* ...... 其餘的css不要刪除*/
}
效果圖:
隨後再遞迴呼叫dorpRain方法,即可生成批量的雨滴以及對應的波紋。
結尾
筆者才疏學淺,慌忙之下難免有遺漏或是疏忽,如有錯誤之處,還望各位看官不吝賜教,筆者在此感謝。
最終的程式碼我放在簡單的下雨效果2.0。
作者:Fatman
部落格園地址:https://www.cnblogs.com/liujingjiu
CSDN地址:https://blog.csdn.net/qq_35508835
版權歸Fatman所有,歡迎保留原文連結進行轉載:)