是谁,在敲打我窗-CSS雨滴动画效果

798 阅读7分钟

1、扯闲篇

是谁在敲打我窗 是谁在撩动琴弦 那一段被遗忘的时光

渐渐地回升出我心坎 是谁在敲打我窗 是谁在撩动琴弦

记忆中那欢乐的情景 慢慢地浮现在我的脑海

那缓缓飘落的小雨 不停地打在我窗 只有那沉默不语的我

不时地回想过去 是谁在敲打我窗 是谁在撩动琴弦

记忆中那欢乐的情景 慢慢地浮现在我的脑海

那缓缓飘落的小雨 不停地打在我窗 只有那沉默不语的我

不时地回想过去 是谁在敲打我窗 是谁在撩动琴弦

记忆中那欢乐的情景 慢慢地浮现在我的脑海

记忆中那欢乐的情景 慢慢地浮现在我的脑海

“是谁,在敲打我窗”这句歌词是出自蔡琴所演唱的《被遗忘的时光》,说起蔡琴,真是个暴露年龄的事。当年我还是青葱少年的时候,看过蔡琴那时老公导的一部电影,我清楚的记得那天是晚上10点半,我打算看完12点睡觉的,结果这部电影长达4个小时,我看完的时候已经是凌晨三点了,我当时很后悔为啥看之前没先看下电影时长。但这部电影可以说是台湾电影中最好的几部之一,《牯岭街少年杀人事件》,导演是杨德昌,顺便提一句,主演是张震。

---------------------------------------扯完闲篇的分割线-------------------------------------

2、玻璃窗

今天我们要实现的是雨滴效果,不过实现雨滴前,我们先把毛玻璃的效果弄出来,没有玻璃窗,雨都进屋了,还有啥好敲打的。

<div class='window'></div>
.window {
            position: absolute;
            width: 100vw;
            height: 100vh;
            background: url("https://cn.bing.com//th?id=OHR.ParrotsIndia_ZH-CN8386276023_UHD.jpg");
            background-size: cover;
            background-position: 100%;
            filter: blur(10px);
        }

其实就是给一张图片,做了个模糊化的效果,看起来像毛玻璃的效果

3、一滴雨

雨滴的效果就很巧妙了,让我们看下一滴雨完整的效果图

这滴雨其实分为两个部分,第一部分是底部的阴影部分,其实是个边框来的,代码如下:

.border {
            position: absolute;
            margin-left: 92px;
            margin-top: 51px;
            border-radius: 100%;
            box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.6);
            transform: rotateY(0);
            width: 20px;
            height: 28px;
        }

<div class='border'></div>

通过width和height属性和border-radius把border拉成一个椭圆形,然后用box-shadow把阴影拉出来,作为水滴的阴影,边框的最终效果如下:

然后是水滴的部分

.raindrop {
            filter: brightness(1.2);
            position: absolute;
            margin-left: 90px;
            margin-top: 50px;
            background-size: 5vw 5vh;
            border-radius: 100%;
            background-image: url("https://cn.bing.com//th?id=OHR.ParrotsIndia_ZH-CN8386276023_UHD.jpg");
            transform: rotate(180deg) rotateY(0);
            background-position: 48.1994160428% 54.3259834959%;
            width: 24px;
            height: 28px;
        }
<div class='raindrop'></div>
  • 水滴用background-image设置了玻璃的图片作为倒影,倒影之所以是倒影,是因为影子是倒着的,所以用transform的rotate()旋转了图片180,正好倒过来
  • 通过background-position设置图片在水滴的显示的位置,后面不同的水滴会显示出的倒影因为图片位置的变化都不同,显得真实
  • raindrop的width比border的width多几个像素,是为了让水滴的两边盖住border,只留border的上下显示阴影
  • raindrop的margin-left和margin-top和border的也是略有不同,是为了raindrop能居中覆盖在border上面,使得水滴和阴影融合的更真实

单独无阴影的水滴效果如下:

4、随机雨滴

雨从来都不是一滴一滴来的,而且也不会很有规律的来,为了让雨滴随机出现在玻璃窗上,我请出了《基努·里维斯、css-doodle、黑客帝国字幕雨和随机艺术》用到过的大杀器,css-doodle框架。

<css-doodle use="var(--rule)"></css-doodle>

先创建一个css-doodle的自定义组件

--rule: ( :doodle {
                    @grid: 10x10/ 100%;
                    overflow: visible;
                }

打出10*10个grid,这样最多可以出现100滴雨

transform: scale(@rand(.5, .9));

通过transform:scale让雨滴随机变大变小

:before {
                        content: '';
                        position: absolute;
                        margin-left: @var(--mleft);
                        margin-top: @var(--mtopb);
                        border-radius: 100%;
                        box-shadow: 0 0 0 @var(--shadow-size) rgba(0, 0, 0, 0.6);
                        transform: rotateY(0);
                        width: @var(--widthb);
                        height: @var(--height);
                    }

                    :after {
                        content: '';
                        filter: brightness(1.2);
                        position: absolute;
                        margin-left: @var(--mleft);
                        margin-top: @var(--mtopa);
                        background-size: 5vw 5vh;
                        border-radius: 100%;
                        background-image: url("https://cn.bing.com//th?id=OHR.ParrotsIndia_ZH-CN8386276023_UHD.jpg");
                        transform: rotate(180deg) rotateY(0);
                        background-position: @r(1%, 100%) @r(1%, 100%);
                        width: @var(--widtha);
                        height: @var(--height);

                    }
  • 这里的:before和:after大家看的是不是很眼熟,其实:before里面的内容就是前面border的样式,:after里面的就是前面raindrop的样式。
  • content必须要有,因为在伪元素(before,after)里如果没有设置“content”属性,伪元素是无用的,整个:before和:after里面的配置就都无效了。
  • 为了让雨滴显得更清晰,加了filter: brightness(1.2)让雨滴显得更亮一些。

这里面比较奇怪的是@var(),它其实就是css的变量,在css-doodle做了一层包装,作用和css的var()是一样的,我们看下这些变量的定义

                    --size:(4 + @r(1, 8));
                    --shadow-size: calc(((@var(--size)*0.3) - 1)*1px);
                    --mleft:@r(1, 100)px;
                    --mtop:@r(1, 100);
                    --mtop1:(@var(--mtop) - 1);
                    --mtopb:calc(@var(--mtop)*1px);
                    --mtopa:calc(@var(--mtop1)*1px);
                    --height:@r(15, 25)px;
                    --width:@r(8, 20);
                    --width1:(@var(--width) + 5);
                    --widthb:calc(@var(--width)*1px);
                    --widtha:calc(@var(--width1)*1px);

这里有几个坑要说明一下:

  • 1号坑:css-doodle提供了@calc(),但是这里计算还是要用css的calc(),使用@calc()无效。
  • 2号坑:在使用@var做加减法的时候,+-号的两遍要有空格,否则计算无效,切记切记。
  • 3号不是坑:计算好数值后,怎么带上px单位呢,用calc(value*1px)这种方法就可以了,其他的单位也可以用这种方法。
  • 4号说明:为什么要用变量?是因为before和after不在一起,为了让boder和raindrop的margin-left、margin-top、width、height等属性能够保持一致,就需要用变量在before和after外面定义好,传值到里面去。

最终的效果如下:

5、没动画效果怎么敲打我窗

雨点如果只是这样洒在窗户,那就没有敲打的意思,为了体现敲打,我决定让雨点动起来。

:before {
                        content: '';
                        position: absolute;
                        margin-left: @var(--mleft);
                        margin-top: @var(--mtopb);
                        border-radius: 100%;
                        box-shadow: 0 0 0 @var(--shadow-size) rgba(0, 0, 0, 0.6);
                        transform: rotateY(0);
                        width: @var(--widthb);
                        height: @var(--height);
                        opacity: 0;
                        animation: raindrop-fall 500ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
                        animation-fill-mode: forwards;
                        animation-delay: @var(--delay);
                    }

                    :after {
                        content: '';
                        filter: brightness(1.2);
                        position: absolute;
                        margin-left: @var(--mleft);
                        margin-top: @var(--mtopa);
                        background-size: 5vw 5vh;
                        border-radius: 100%;
                        background-image: url("https://cn.bing.com//th?id=OHR.ParrotsIndia_ZH-CN8386276023_UHD.jpg");
                        transform: rotate(180deg) rotateY(0);
                        background-position: @r(1%, 100%) @r(1%, 100%);
                        width: @var(--widtha);
                        height: @var(--height);
                        opacity: 0;
                        animation: raindrop-fall 500ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
                        animation-fill-mode: forwards;
                        animation-delay: @var(--delay);
                    }

重点是:before和:after里面的最后三行,是用来实现动画效果的。

  • cubic-bezier()控制动画的速度,使得雨滴落到玻璃窗的效果更逼真
  • animation-delay 雨点出现的时间随机,同样是为了效果更逼真,逼真的效果真麻烦

再来看看raindrop-fall的@keyframe设置

               @keyframes raindrop-fall {
                    0% {
                        opacity: 0;
                        transform: rotate(180deg) scale(2.5, 2.3) rotateY(0);
                    }

                    100% {
                        opacity: 1;
                        transform: rotate(180deg) scale(1, 1) rotateY(0);
                    }
                }

最后的动画效果如下:

在线效果

源代码地址

番外篇

阿恺:是谁,在敲打我窗~

妈妈:有谁来了吗

阿恺:没有,是下雨

妈妈:不像啊,好像是有人在敲窗户

阿恺:是很大的雨点而已,是谁,在敲打我窗~

窗外的声音:开门,敲了半天都没人理

阿恺:你是谁

窗外的声音:查水表的...

阿恺:为什么不敲门?

窗外的声音:门口堆满了东风快递...