有趣的css—简单的下雨效果2.0版

简单的下雨效果 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>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"rain"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

</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;
<span class="hljs-keyword">function</span> <span class="hljs-title function_">dorpRain</span>(<span class="hljs-params"></span>){
    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">if</span>(<span class="hljs-keyword">typeof</span> clientWidth !== <span class="hljs-string">'undefined'</span> &amp;&amp; <span class="hljs-literal">null</span> !== clientWidth){
            <span class="hljs-keyword">let</span> el = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">createElement</span>(<span class="hljs-string">'div'</span>);
            el.<span class="hljs-title function_">setAttribute</span>(<span class="hljs-string">'class'</span>, <span class="hljs-string">'raindrop'</span>);
            <span class="hljs-keyword">let</span> left = <span class="hljs-built_in">parseInt</span>(<span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() * clientWidth, <span class="hljs-number">10</span>) + <span class="hljs-string">'px'</span>;
            el.<span class="hljs-property">style</span>.<span class="hljs-property">left</span> = left;
            rain.<span class="hljs-title function_">appendChild</span>(el);

            <span class="hljs-keyword">let</span> time = <span class="hljs-built_in">parseInt</span>(<span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() * <span class="hljs-number">350</span>, <span class="hljs-number">10</span>);

            <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
                rain.<span class="hljs-title function_">removeChild</span>(el);

                <span class="hljs-keyword">let</span> newEl = <span class="hljs-variable language_">document</span>.<span class="hljs-title function_">createElement</span>(<span class="hljs-string">'div'</span>);
                newEl.<span class="hljs-title function_">setAttribute</span>(<span class="hljs-string">'class'</span>, <span class="hljs-string">'ripple'</span>);
                newEl.<span class="hljs-property">style</span>.<span class="hljs-property">left</span> = left;
                newEl.<span class="hljs-property">style</span>.<span class="hljs-property">top</span> = <span class="hljs-built_in">parseInt</span>(clientHeight / <span class="hljs-number">100</span> * <span class="hljs-number">50</span> + (time / <span class="hljs-number">350</span> * (clientHeight / <span class="hljs-number">100</span> * <span class="hljs-number">50</span>)), <span class="hljs-number">10</span>) + <span class="hljs-string">'px'</span>;
                rain.<span class="hljs-title function_">appendChild</span>(newEl);

                <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
                    rain.<span class="hljs-title function_">removeChild</span>(newEl);
                }, <span class="hljs-number">600</span>)
            }, <span class="hljs-number">400</span> + time, <span class="hljs-number">10</span>)
        }
    }, <span class="hljs-built_in">parseInt</span>(<span class="hljs-number">10</span> + <span class="hljs-title class_">Math</span>.<span class="hljs-title function_">random</span>() * <span class="hljs-number">10</span>, <span class="hljs-number">10</span>)) 
}
<span class="hljs-title function_">dorpRain</span>();

}

在这里插入图片描述

使波纹以水滴消失位置为圆心扩散

其实到这一步还是有个问题,就是波纹是向右下方扩散的,下面是笔者将动画时间增大 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>
<span class="hljs-tag">&lt;<span class="hljs-name">style</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/css"</span>&gt;</span><span class="language-css">
    * {
        <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
    }

    <span class="hljs-selector-tag">div</span> {
        <span class="hljs-attribute">width</span>: <span class="hljs-number">100px</span>;
        <span class="hljs-attribute">height</span>: <span class="hljs-number">100px</span>;
        <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fe0000</span>;
        <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">20px</span>;
    }

</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"transform: rotateX(75deg);"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>

</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所有,欢迎保留原文链接进行转载:)