Making fancy demos using only CSS might not be useful per se. However, it’s a good opportunity to explore the capabilities of CSS, to try new tools, and to practice working within constraints. Today, we will take a look at how to make a raindrops on window effect, using HAML and SASS.
View the CodePen demo.
Preprocessors
First of all, let me explain why we’re using HAML/SASS instead of plain HTML/CSS. Even though they are simply useful all-around, the reason we need preprocessors here is that they allow us to, among other things, use variables, create loops, and generate random values, so we don’t have to deal with hundreds of raindrops individually by hand.
More information about setup and syntax can be found on their respective webpages. Alternately, you can try this demo on Codepen, creating a new pen, and then selecting SCSS for the CSS preprocessor and HAML for HTML.
The Window
The first step is to display the window itself.
.container
.window
// Our background image.
// Will be used for the window as well as
// for the raindrops themselves
$image: 'http://i.imgur.com/xQdYC7x.jpg';
// container width and height.
// 100vw/vh so it fills the entire window
$width:100vw;
$height:100vh;
.container{
position:relative;
width:$width;
height:$height;
overflow:hidden;
}
.window{
position:absolute;
width:$width;
height:$height;
background:url($image);
background-size:cover;
background-position:50%;
filter:blur(10px);
}
Here we simply draw a div
with the background image of your choice. We are also applying a blur filter to it so the drops will become more visible.
Notice that we are storing the background image URL on the variable $image
. We are doing this because we’ll use the same image for the raindrops themselves. More on that later.
In Real Life
Before we proceed, let’s take a look at how a raindrop on a window looks in real life:
Source: Wikipedia
Due to refraction, the raindrop flips the image behind it. Also, when its shape is more or less that of a half sphere, it looks like it has a black border.
Raindrops
Based on what we’ve seen, let’s try to create a single raindrop:
.container
.window
.raindrop
$drop-width:15px;
// our raindrops are not going to be perfectly round, so we will stretch them a bit
// (not using transform:scale so our background doesn't get stretched)
$drop-stretch:1.1;
$drop-height:$drop-width*$drop-stretch;
.raindrop{
position:absolute;
top:$height/2;
left:$width/2;
width:$drop-width;
height:$drop-height;
// border radius 100% instead of 100px, so the raindrop is elliptical rather than a capsule-shaped
border-radius:100%;
background-image:url($image);
background-size:$width*0.05 $height*0.05;
transform:rotate(180deg);
}
This is pretty straightforward: all we do is draw an elliptical div
, fill it with the background image we used before, scale the background down, and turn the object upside down.
Now we’re going to add a little border around it, to make it look like the raindrop has volume.
...
.border
.raindrop
...
.border{
position:absolute;
top:$height/2;
left:$width/2;
margin-left:2px;
margin-top:1px;
width:$drop-width - 4;
height:$drop-height;
border-radius:100%;
box-shadow:0 0 0 2px rgba(0,0,0,0.6);
}
Notice that we didn’t just add a perfect border around the drop, we shifted and squished it a bit so it looks more natural.
So now that our raindrop is looking pretty good, let’s see how we can add hundreds of them.
...
.raindrops
.borders
- (1..100).each do
.border
.drops
- (1..100).each do
.raindrop
This is the HAML syntax for loops. Here we are simply adding a hundred .raindrop
and .border
objects inside their respective wrappers.
The SASS part is trickier, so will look at it step by step.
First, here’s how we’ll create our loop and select each element individually:
@for $i from 1 through 100{
// using the $i variable with the CSS nth-child pseudo-class
.raindrop:nth-child(#{$i}){
}
.border:nth-child(#{$i}){
}
}
Now, we’ll generate and apply random positions and sizes to our raindrops:
@for $i from 1 through 200{
// generates a random number from 0 to 1, for the positioning
$x:random();
$y:random();
// Random raindrop size and stretching.
// Since each raindrop has different sizes, we'll do our sizing
// calculations here instead of on the main .raindrop selector
$drop-width:5px+random(11);
$drop-stretch:0.7+(random()*0.5);
$drop-height:$drop-width*$drop-stretch;
.raindrop:nth-child(#{$i}){
// multiply the random position value by the container's size
left:$x * $width;
top:$x * $height;
width:$drop-width;
height:$drop-height;
}
.border:nth-child(#{$i}){
// we'll do the same positioning for the drop's border
left:$x * $width;
top:$x * $height;
width:$drop-width - 4;
height:$drop-height;
}
}
Finally, there’s an important detail missing: we have to change the position of the background of each raindrop according to the drop’s position, so our refraction effect looks nice.
...
.raindrop:nth-child(#{$i}){
...
background-position:percentage($x) percentage($y);
}
...
And with that, the main effect is done! We can then add small details, like increasing the brightness of the raindrops a bit to make them look shiny, animate them falling, change the focus, change the image, and so on.
Here’s the demo of the version we made here, along with the complete code:
View the CodePen demo.
Hope you could learn something useful from this tutorial, and hope you enjoyed it!
Thaddeus says:
Love it. I made it rain:
http://codepen.io/tiger2380/pen/doNxQX
h3 says:
Very clever, nice work
Matt Perry says:
Awesome example, here it is animated using Redshift (http://redshiftjs.com)
http://codepen.io/SirHound/pen/rVyxpP
It would be easy to make it adjust the background position too, but probably not so easy on your processor.
Mauricio Lanner says:
Wow it was very good
kirti says:
.Yeahhhhhh its really a clever trick……..nice…….