是誰,在敲打我窗-CSS雨滴動畫效果
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);
}
}
最後的動畫效果如下:
番外篇
阿愷:是誰,在敲打我窗~
媽媽:有誰來了嗎
阿愷:沒有,是下雨
媽媽:不像啊,好像是有人在敲窗戶
阿愷:是很大的雨點而已,是誰,在敲打我窗~
窗外的聲音:開門,敲了半天都沒人理
阿愷:你是誰
窗外的聲音:查水錶的...
阿愷:為什麼不敲門?
窗外的聲音:門口堆