-
CSS3 animations demonstrated with snow
This winter we have had an unusually snowy time of things, as a matter of fact so has the whole of the UK. As a Clearleft christmas snowy surprise I wrote a CSS3 snow bookmarklet.
If you are running a WebKit based browser you can make it snow on anywhere on the internet by dragging the following bookmarklet into your toolbar, sending any site into a mini snowstorm!
I based the snow in part on the leaf example on the WebKit blog.
JavaScript is only just to add the snowflakes in their starting position (and naturally in order for it to be a bookmarklet) but all the animation and styles are done in pure CSS. So if you wanted to do this without any JavaScript at all you could just start with the snowflake elements directly in the source and it would work fine.
For the snowflakes themselves I had a lot of fun looking through the Unicode list of snowflakes, and asterisks trying to find the prettiest snowflakes ❆, ❅, ❄, ✳, they are in an array and the sizes are in another array. Every snowflake is an absolutely positioned div with a span inside, we will come to that later. When a snowflake is chosen a size and a character are chosen at random. the distribution of sizes is controlled by the proportion of the size class in the array:
var flakes = ['2746', '2745', '2744', '2733']; var sizes = ['tiny', 'tiny', 'tiny', 'small', 'small', 'small', 'small', 'medium', 'medium', 'medium', 'medium', 'medium', 'medium', 'large', 'massive'];When the snowflakes are positioned, a random side (left or right) and a percentage between 0 and 60 is chosen to set the position absolute values, this makes it work nicely when you resize the browser and means there is a higher concentration of snowflakes in the center 20% of the screen. They also needed an epic-ly large z-index because this is going to be used as a bookmarklet and you don't know what will be on the page it is used on.
Another consideration in the setup was that white snowflakes would not be seen on white pages so a text shadow was added to make it stand out, I used rgba for this to make it blend in better with any background color.
text-shadow: rgba(0, 0, 0, 0.7) 1px 1px 2px;There are two main animations for each snowflake, fading and dropping. The span inside the snowflake does the spinning. These animations are set up and given a reference-able name in the CSS file:
/* Hides a snowflake towards the very end of the animation */ @-webkit-keyframes fade { /* Show a snowflake while into or below 85 percent of the animation and hide it, otherwise */ 0% { opacity: 1; } 85% { opacity: 1; } 100% { opacity: 0; } } @-webkit-keyframes drop { /* Makes a snowflake fall from -50px to 650px pixels in the y-axis starting at -50 so as to appear to be falling from above the screen not starting at the top */ 0% { -webkit-transform: translate(0px, -50px); } 100% { -webkit-transform: translate(0px, 650px); } }The named animation group contains sub groups, rule blocks where the selector is one moment in the animation. When the animation runs it looks at the timeline of keyframes and smoothes the animation accordingly, for example in the fade animation above, the opacity stays at 1 until 85 percent through its animation (aka one drop) where it then begins a smooth fade to 0 over the last 15% of the time. If we had omitted that middle step and just had opacity set to 1 at 0% and to 0 at 100% then the fade would be a smooth fade over the duration of the whole animation.
You can also set the 'moment' of the keyframe using the keywords from and to, this is effectively the same as 0% and 100%. For simple animations this probably would suffice but I find using percentages gives you a greater degree of control over the pace of your animation.
-webkit-animation-name: 'fade, drop'Because we know we have two animations being applied to each snowflake div in JavaScript we can give the other animation properties two values and know that they apply to the two animations respectively.
-webkit-animation-iteration-count: infinite, 20;The
animation-iteration-countstates how many times an animation should run. We are not too fussed about the fade but we want to only have each snowflake fall from the sky 20 times before disappearing, just in case the bookmarklet is used and left open in a tab it shouldn't continue indefinitely.-webkit-animation-direction: normal, normal;Options for this are normal and alternate. Normal behaves exactly as you have specified in your keyframes, alternate behaves normally on odd iterations and reversed on even iterations
-webkit-animation-timing-function: linear, ease-in;There are quite a few options you can play with for this property
easelinearease-inease-outease-in-outand if you really know your animation maths you can go wild withcubic-bezier(X, X, X, X). Basically this describes the acceleration of the animation e.g.linearis all at the same speed,ease-inhas a little warm up first,ease-outwarms down andease-in-outdoes both. I wanted the snowflakes to ease gradually in before reaching a constant speed.-webkit-animation-durationWe are setting the animation-duration in Javascript because it needs to be randomised to avoid all the flakes falling at the same rate. This is being set at a random time between 5 and 11 seconds (trial and error got these magic numbers). Ideally I would have set the range based on the size of the snowflake which would have been cool, I could easily have made the larger ones fall faster than smaller ones, but I didn't think of that at the time.
-webkit-animation-delayHow long until the first snowflake starts falling? I have set this in Javascript to be instant for the first snowflake (remember it still has to fall 'into the screen' when it starts) and then a random amount of time between 4 and 8 seconds for the others. This was important with so few snowflakes because we need to spread them out so they don't all fall at the same time and look like a continuous snow storm
Naturally there is an animation shorthand property should you wish to specify everything at once.
With our snowflakes falling and fading nicely, all that remains is the spinning. in the JavaScript I set a random animation to the span inside the snowflake div that contains the snowflake character. The animation is then either clockwiseSpin or counterclockwiseSpin
@-webkit-keyframes clockwiseSpin { /* Rotates a snowflake from -50 to 50 degrees in 2D space */ 0% { -webkit-transform: rotate(-50deg); } 100% { -webkit-transform: rotate(50deg); } } @-webkit-keyframes counterclockwiseSpin { /* Rotates it from 50 to -50 degrees in 2D space */ 0% { -webkit-transform: rotate(50deg); } 100% { -webkit-transform: rotate(-50deg); } }We then set the properties explained above for the span.
-webkit-animation-iteration-count: infinite; -webkit-animation-direction: alternate; -webkit-animation-timing-function: ease-in-out; -webkit-transform-origin: 50% -100%;The spinning should be infinite and to add to randomness we need to use the 'alternate' value for direction and ease-in-out as the timing function, the offset transform origin plays with the rotation transform inside the spinning animation in order to make it seam like it is floating on a light snowy breeze.
To finish it off then the JavaScript is wrapped in a self executing function, the CSS is added to the head of the document from the main JavaScript file rather than having to bulk that into the bookmarklet. The JavaScript on is then splurted into the head of the document. The bookmarklet looks like this:
javascript:(function(){var%20s=document.createElement('script');s.src='http://j.mp/7ZZuRu';document.getElementsByTagName('head')[0].appendChild(s);})()All that remains is to just Let it Snow!
Further reading
These two articles on the WebKit blog have some good demo's on the use of animation. My 24ways article goes into a bit more detail on transforms and Tim Van Damme's great 24ways article has a supurb space animation demo which is worth looking at. The spec is available on the w3c site.
