Adobe Creative Cloud

June 1, 2015 /Code /

CSS-Only Raindrops on Window Effect

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);
}

window-optimized

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:

real

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);
}

borderless-optimized

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);
}

bordered-optimized

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:$y * $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:$y * $height;

        width:$drop-width - 4;
        height:$drop-height;
    }
}

raindrops-nopos-optimized

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);
}
...

raindrops-nofilter-optimized

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!

Code

Join the discussion

  • By Thaddeus - 9:51 PM on June 1, 2015  

    Love it. I made it rain:
    http://codepen.io/tiger2380/pen/doNxQX

  • By h3 - 2:36 AM on June 2, 2015  

    Very clever, nice work 🙂

  • By Matt Perry - 3:28 PM on June 2, 2015  

    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.

  • By Mauricio Lanner - 7:13 PM on June 3, 2015  

    Wow it was very good

  • By kirti - 5:44 AM on June 5, 2015  

    .Yeahhhhhh its really a clever trick……..nice…….

  • By Arie - 7:52 PM on June 9, 2015  

    This is awesome. Not as dynamic, but check out this method for generating this effect for video in Adobe After Effects. http://stavchansky.net/blog.php?bID=20

  • By KatieK - 4:50 PM on August 24, 2015  

    In case anyone else runs into a problem where all of the raindrops are in a diagonal line across the screen, there’s an error to fix in the article’s code. To fix it, change `top:$x * $height;` to `top:$y * $height;` in the `.raindrop:nth-child(#{$i}) {` selector.

    • By Lucas Bebber - 12:02 PM on August 31, 2015  

      Oh, you’re right! I had fixed it in the demo, but forgot to fix it in the article. Thanks for pointing it out!