CSS 3D Clouds Retake

This post is going to cover my experience following through the tutorial on making CSS 3D clouds posted here: http://www.clicktorelease.com/tutorials/css3dclouds/. I didn’t make the original code, but I did run into several issues while I went through and I wanted to share my experience, work-arounds, and pieces of code that were missing from the original tutorial.

All the questions that came up and fixes changes here were done on the Chrome Beta (v20.0.1132.41 beta-m to be exact)

1. Creating the world and a camera

CSS 3D Clouds Step 1

In this step, you create two div’s in the body of your HTML 5 page, the outer <div> gets an id of viewport and the inner <div> gets an id of world. From there you setup some structural styling (viewport spans entire screen via absolute positioning, world is centered in the middle)

Initial Page Setup

Some initial styling needs to be done to make it look like the demo, here’s what I have:

  1. * {
  2. box-sizing: border-box;
  3. margin: 0;
  4. padding: 0;
  5. }
  6.  
  7. body {
  8. overflow: hidden;
  9. }
  10.  
  11. #viewport {
  12. background-image: linear-gradient(bottom, rgb(69, 132, 180) 28%, rgb( 31, 71, 120 ) 64%);
  13. background-image: -o-linear-gradient(bottom, rgb(69, 132, 180) 28%, rgb( 31, 71, 120 ) 64%);
  14. background-image: -moz-linear-gradient(bottom, rgb(69, 132, 180) 28%, rgb( 31, 71, 120 ) 64%);
  15. background-image: -webkit-linear-gradient(bottom, rgb(69, 132, 180) 28%, rgb( 31, 71, 120 ) 64%);
  16. background-image: -ms-linear-gradient(bottom, rgb(69, 132, 180) 28%, rgb( 31, 71, 120 ) 64%);
  17. }
  18.  
  19. #world {
  20. background-color: rgba(255, 0, 0, .2);
  21. }

Vendor prefixes

I’m so used to Chrome being the “latest and greatest” that I honestly expected to be able to use non-prefixed CSS properties and have the code “just work”. That’s NOT the case. As of this writing you will need to use prefixed properties, so add the following prefixes into the appropriate rules:

  1. #viewport {
  2. perspective: 400;
  3. -webkit-perspective: 400;
  4. -moz-perspective: 400;
  5. -o-perspective: 400;
  6. }
  7.  
  8. #world {
  9. transform-style: preserve-3d;
  10. -webkit-transform-style: preserve-3d;
  11. -moz-transform-style: preserve-3d;
  12. -o-transform-style: preserve-3d;
  13. }

Help, my javascript code doesn’t work!

You probably put your javascript in the <head> tag, which means that

  1. document.getElementById( 'world' )

will not work because the elements don’t exist yet. Put the script at the end right before the </body> tag and it should work if everything else is correct.

Besides, it’s just good practice to put your javascript last.

Help, my javascript code doesn’t work! (pt 2)

This just shows my ignorance of javascript, but if something still isn’t working, you might have this problem:

Javascript uses the \ character in your strings to tell the parser to treat the next line as if the string continued:

  1. 'translateZ( ' + d + 'px ) \
  2. rotateX( ' + worldXAngle + 'deg) \
  3. rotateY( ' + worldYAngle + 'deg)';

Is the same as:

  1. 'translateZ( ' + d + 'px ) rotateX( ' + worldXAngle + 'deg) rotateY( ' + worldYAngle + 'deg)';

Zooming javascript

The code samples in the original tutorial omit the code to zoom in and out with the mouse wheel. Here it is in all it’s javascripty wonderfulness:

  1. window.addEventListener( 'mousewheel', onContainerMouseWheel );
  2. window.addEventListener( 'DOMMouseScroll', onContainerMouseWheel );
  3.  
  4. function onContainerMouseWheel( event ) {
  5. event = event ? event : window.event;
  6. d = d - (event.detail ? event.detail * -5 : event.wheelDelta / 8);
  7. updateView();
  8. }

2. Adding objects to our world

CSS 3D Clouds Step 2
E.g. .cloudBase.

Create cloud base code is incorrect

Instead of:

  1. for( var j = 0; j <<; 5; j++ ) {

the correct line is:

  1. for( var j = 0; j < 5; j++ ) {

Actual random numbers and prefixed transforms

The random numbers for createCloud():

  1. var random_x = 256 - ( Math.random() * 512 );
  2. var random_y = 256 - ( Math.random() * 512 );
  3. var random_z = 256 - ( Math.random() * 512 );

The transform styles for createCloud()

  1. div.style.transform = t;
  2. div.style.webkitTransform = t;
  3. div.style.MozTransform = t;
  4. div.style.oTransform = t;

3. Adding layers to our objects

CSS 3D Clouds Step 3
There were a couple of things in this section that cause me to scratch my head and go whyyyyy?

Code for random generation and transforms.

Random variables:

  1. var random_x = 256 - ( Math.random() * 512 );
  2. var random_y = 256 - ( Math.random() * 512 );
  3. var random_z = 100 - ( Math.random() * 200 );
  4. var random_a = Math.random() * 360;
  5. var random_s = .25 + Math.random();
  6. random_x *= .2; random_y *= .2;

Vendor transforms:

  1. cloud.style.transform = t;
  2. cloud.style.webkitTransform = t;
  3. cloud.style.MozTransform = t;
  4. cloud.style.oTransform = t;

Why don’t I see the new squares?

You have to add in the style for .cloudLayer into your CSS:

  1. .cloudLayer {
  2. position: absolute;
  3. left: 50%;
  4. top: 50%;
  5. width: 256px;
  6. height: 256px;
  7. margin-left: -128px;
  8. margin-top: -128px;
  9. background-color: rgba( 0, 255, 255, .1 );
  10. -webkit-transition: opacity .5s ease-out;
  11. -moz-transition: opacity .5s ease-out;
  12. -o-transition: opacity .5s ease-out;
  13. }

I see the cloud layers, but why are they are all flat?

Yeah, this got me too, the parent div’s need to have preserve-3d, so add this into your CSS:

  1. #world div {
  2. transform-style: preserve-3d;
  3. -webkit-transform-style: preserve-3d;
  4. -moz-transform-style: preserve-3d;
  5. -o-transform-style: preserve-3d;
  6. }

4. Making the 3D effect work

CSS 3D Clouds Step 4

This section is essentially “make the layers point at the camera”. You still want them projected into the same locations, but you want them to always face the camera, giving you that sense of “volume” effect.

Vendor Transforms and Update()

First, here’s all the vendor transforms:

  1. layer.style.transform = t;
  2. layer.style.webkitTransform = t;
  3. layer.style.MozTransform = t;
  4. layer.style.oTransform = t;

Now, you also need to call this update manually once right before the end of your script. So right before the closing script tag, make sure you call this:

  1. update();

Render Loop

Finally, even if you do this, you’ll notice that your layers still don’t point at the camera. You need to add in a function that loops and updates the layers in the #viewport at regular intervals. You could add a call to the update function inside your mouse move event, but we’ll need the loop to get the rotation to work in the next step, so it’s better if you just do this now.

  1. (function() {
  2. var lastTime = 0;
  3. var vendors = ['ms', 'moz', 'webkit', 'o'];
  4. for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
  5. window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
  6. window.cancelRequestAnimationFrame = window[vendors[x]+
  7. 'CancelRequestAnimationFrame'];
  8. }
  9.  
  10. if (!window.requestAnimationFrame)
  11. window.requestAnimationFrame = function(callback, element) {
  12. var currTime = new Date().getTime();
  13. var timeToCall = Math.max(0, 16 - (currTime - lastTime));
  14. var id = window.setTimeout(function() { callback(currTime + timeToCall); },
  15. timeToCall);
  16. lastTime = currTime + timeToCall;
  17. return id;
  18. };
  19.  
  20. if (!window.cancelAnimationFrame)
  21. window.cancelAnimationFrame = function(id) {
  22. clearTimeout(id);
  23. };
  24. }())

Lets break this down. I discovered that this function is a polyfill for the browser animation spec here one Paul Irish’s awesome blog http://paulirish.com/2011/requestanimationframe-for-smart-animating/. Previously, when you would animate something, you would set a timer, and every few milliseconds go and update move something from point A to point B on the screen with a small increment. In order to get smoother animations, the browsers are starting to support this requestAnimationFrame function that allows several changes to be made, and update everything with a single reflow / redraw of the screen. This becomes especially handy when you are updating multiple elements on the screen and you want a clean responsive display. It also means that the browser can stop animating when you switch tabs, which means that you don’t eat up battery when someone isn’t looking at your page :)

All you really need to know is that this creates a function on the javascript window object that tells the browser to “please render an animation frame, and call back to this function when you are done and ready to render the next frame”.

5. Final words

CSS 3D Clouds Step 5

Subtle Rotating

Not mentioned, but if you want the clouds to slowly rotate like the demo, you need to add in the rotate z component into the transform update of your layers like so:

  1. 'rotateZ( ' + layer.data.a + 'deg ) /

And, you need to add in a speed property into your cloud layers when you create them:

  1. speed: .2 * Math.random() - .1

Adding Cloud Images

Instead of this:

  1. var cloud = document.createElement( 'div' );

Use this (or find your own cloud .png, it should be transparent to work properly)

  1. var cloud = document.createElement( 'img' );
  2. cloud.style.opacity = 0;
  3. var src = 'http://www.clicktorelease.com/tutorials/css3dclouds/cloud.png';
  4. ( function( img ) {
  5. img.addEventListener( 'load', function() {
  6. img.style.opacity = .8;
  7. } )
  8. } )( cloud );
  9. cloud.setAttribute( 'src', src );

And finally to remove all the debug styles remove the following lines out of your CSS:

From #world remove:

  1. background-color: rgba(255, 0, 0, .2);

From .cloudBase remove:

  1. background-color: rgba( 255, 0, 255, .5 );

From .cloudLayer remove:

  1. background-color: rgba( 0, 255, 255, .1 );

That should cover it! Now go make some happy clouds.

Credits

Everything here was completely taken from the Click To Release CSS 3D Clouds tutorial and expanded to include the missing parts.

On Modern Layouts and Semi Fluid Grids

Cross post: This article has been cross posted from http://blogs.interknowlogy.com/2011/10/25/on-modern-layouts-and-semi-fluid-grids/

Over the last several weeks, I’ve really been digging into HTML5, CSS3, and really figuring out what modern browsers can do. Frankly browsers have grown by leaps and bounds over the last several years, and as such we need to re-evaluate how we build websites. Especially in regards to how we treat older browsers. I recently read this post by Paul Irish and he points out that ideally, each browser gets an experience that is customized to that browser’s capabilities. What this means however, is that we should expect older browsers to get an experience that isn’t exactly the same as the experience we get on a modern one. (Example, no rounded corners. No text shadows. Etc…)

Let me try to explain:

A really good analogy to this is how HD TVs have changed the TV industry. Imagine for a moment that a website site is the film, and the TV is your browser. If you had a black and white TV, you wouldn’t expect to see the film in color. If you have a standard definition TV you don’t expect to see the full wide screen movies, and if you have a widescreen 720p TV, you don’t expect to experience the full effect of 1080p Blu-ray. Filmmakers don’t shoot in standard definition and then try to ‘upgrade’ the film to HD quality. Its pointless and a LOT of extra work. No, they shoot with the best film and resolution they can afford and then cut it down to the lower denominators when or if they support older formats.

In the same way, if you use an older browser you wouldn’t expect to get the full experience a modern browser supplies, and moreover, you likely wouldn’t even know what your missing! You don’t expect it since any other site you visit also doesn’t have those features or details. Because of this, you should be designing for the ‘1080p’ of the web, and accept that users with older browsers will still get the ‘standard definition’ experience.

The Web Is Not Print

There’s also an important distinction between print and the web. The web is dynamic and fluid, highly interactive. The web is also delivered on a range of sizes and devices, from something the size of a business card up to large 30″ monitors. There comes a point where form and function have to merge in a non-trivial way to provide an experience that fits itself to the users needs, when they need it, the way they need it.

Print isn’t.

Print is extremely static, once it’s designed, it stays that way. If you design a poster, you design it to be printed at a certain size or at certain proportions. If you design a book or a magazine, you have total control over the size and layout of your spread. If you continue to think about the web this way, you’ll miss much of the dynamic nature of the web that makes it attractive as a medium.

Its only recently we’ve really gained the tools and techniques we need to REALLY break out and away from the classic printers layout due largely to the rise in mobile devices, HTML5 and CSS3. What I’m calling this “Print Format” tends to reveal itself at the extreme edge cases.

Specifically:

  • On large monitors where content will appear tiny and hard to read with lots of extra white space.
  • On small monitors or mobile devices when the content doesn’t scale down and your forced to scroll back and forth to read.

Over the years one of the only real solutions to address this, if someone addressed it at all, was just to never set a width for your content and just let it fill your entire screen. Although this almost works for some things, it’s difficult to read as paragraphs will stretch across the screen. You will constantly lose your place as your eyes track back and forth to the beginning of the next line. Also, if your designing a static site and you and your designers both have large screens, it’s easy to ‘fill up the space’ with ancillary or useless content and ads to make the site feel balanced. All in all, most so called solutions were hacks on a broken system.

Semi Fluid Layout

I’m finally starting to see some CSS frameworks and sites that are really starting to apply these principles. An excellent site that demonstrates this principle is the following:

www.goldengridsystem.com

No matter what browser you visit it on, or what size your browser is at, the content will be sized appropriately, the typography will be readable, and everything will feel right. Sans insane urges to resize your browser window.

One of the key benefits to designing a fluid theme is the instantly correct look you get when you open the site. Its not something people will or should consciously expect, it should simply BE the correct size the instant it appears, regardless of size, screen, or orientation. Its like walking into a store. A well designed building is easy to navigate, spacious, and consistent across all other stores you visit regardless of size. You don’t ever think about it, you’re not there to analyze the layout of the store. You’re there to buy groceries. Anything that gets in the way or makes it hard to find what you’re looking for will stick out, and if it’s bad enough, you’ll go somewhere else. Its the same way with a site or blog. Users are there for a reason: For information, or to read, or to consume whatever content you are serving up. They didn’t come to analyze your site and how it’s designed, but if they have any difficulty at all in getting to what they want in whatever form they want it in, they’ll be somewhere else in about 8 seconds.

It’s also important to be intentional about the decisions your making and how they will affect the final layout of a site. The more you reduce the complexity of the interactions, and the better defined the interactions are, the better the final end result will be. Use what you design. Do what your users do. You’ll know best what you do and don’t like and what feels right. Go with it, but don’t be afraid to stop, backtrack, or even completely scrap good ideas if they don’t fit with what you expect from the design.

Fluid Layout First

Something that’s going to become really important is to start out your design efforts knowing your designing for a fluid site like this. Laying out the groundwork and page structure is going to be extremely important to your long term sanity as a developer. There’s this concept in computer science called emergent behavior, essentially, the less you specify about a system, and by imposing fairly simple rules you can get easily get complex behavior. By starting with the containers, and then working your way to the content I have much less to worry about since most of the content takes care of itself without much more intervention from the developer.

If your someone who already has a well established, static width layout, its going to be difficult to establish a well behaved fluid grid system without tweaking MANY aspects of the site. Its probably why a lot of companies simply serve up an entirely different set of HTML and CSS for the site for mobile devices: Its easier to do than to rebuild their main site to scale all the way down to small screens.

Baseline Grid Script

As I’ve been working on the comatose theme, I’ve been doing my homework and some research into typesetting and vertical rhythm (http://24ways.org/2006/compose-to-a-vertical-rhythm and http://www.alistapart.com/articles/settingtypeontheweb/). Similar to how a lined piece of paper works, I want to get the styling of my content to line up as closely as I can to those lines, in most cases. To do that, I went looking for something that would overlay a simple, custom spaced ‘lined piece of paper’ if you will over my site.

For once, I wasn’t able to find something suitable for what I wanted. Annoyingly, almost all the solutions I could find were some variation of ‘make a grid image in photoshop and tile it on an overlay’. While quaint, I wanted something I could easily tweak the baselines on without making several different images. So, my quest resulted in a little script that uses HTML5 canvas drawing and jQuery to build up a baseline grid, a little magic to figure out the baseline and font size, and a button easily switch it on or off for whatever site its put into. I’m considering turning it into a chrome plug in at some point to allow me to use it on any site, but for now you can use it as is.

For your perusal and tweaking pleasure:

http://gist.github.com/1270673#file_type_setter.js

To use, include the following lines in the header of your html, make sure jQuery is imported somewhere before this:

<script src=”typesetter.js” type=”text/javascript”></script>