6 July 2011

My Avatar

by Geoffrey Grosenbach & Paula Lavalle

  • Avatar
  • Avatar
  • Avatar
  • Avatar
  • Avatar
  • Avatar

The Hooligan

In 2004 I worked at a grade school that hosted a highly anticipated field day every year. I decided to paint my face with my team’s flag for that year: South Africa.

For the next few weeks, kindergarteners told me they had nightmares because of it.

The following year, I coached the team representing the USA. I carefully painted a flag on my face, only later to realize that I had put the stars on the wrong side (damn mirrors!). Fortunately no one noticed since I had started a trend. Almost every kid arrived with some kind of paint splashed on their face: stars, hearts, stripes, camouflage.

I used a photo from that day as my avatar for the next few years, but I managed to lose the original. Instead of adopting an entirely new avatar, I decided it was time to recreate it…but better.

A studio flash, a digital SLR, and some face crayons solved the problem. I’m now the owner of what may be the world’s first studio photograph taken purely for the purpose of being displayed at 48×48 on Twitter, Facebook, GitHub, and other services.

People frequently tell me “You look nothing like your avatar.” Hopefully that’s a little less true now.

Geoffrey Grosenbach (@topfunky)

Colophon

CSS Masks

In order to achieve excruciatingly crisp curves around the images, we used CSS masks with SVG paths.

24-bit PNG images can be saved with transparency without the need for masks, but JPG photos can’t. Using a compressed JPG image with a CSS mask gives you the best of both worlds.

Webkit

Webkit’s masking is more full-featured than Firefox (see notes below). It can use SVG files straight from Adobe Illustrator and can stretch masks dynamically.

CSS image mask

The HTML markup references square images (seen at left). A circular mask is applied. The result is an image that is opaque in the black parts of the mask and transparent in the other parts.

  • An element can be masked directly, or an outer element can mask all the elements inside it.
<li>
  <img src="face.jpg" />
</li>
  • In Adobe Illustrator or another graphics application, draw a black shape and “Save As” SVG. You can use it directly in Webkit with no further modification.
  • Use the -webkit-mask-box-image directive in CSS to specify the path to the SVG file. We found it easiest to use the stretch option to expand the mask to fill the area covered by the element being masked. This also works better for animation since the mask grows as the image is resized.
li {
  -webkit-mask-box-image: url(mask.svg) 0 stretch;
}

Firefox

Firefox needs a slightly different SVG file and can’t stretch the mask.

  • Make a copy of the SVG file you used with Webkit. Open it in a text editor. Modify it to include a mask element around the drawing directives. The mask element should have an id so you can refer to it in the next step (we used avatar-mask).
<!-- Note: Simplified for educational purposes.  -->
<svg>
  <mask id="avatar-mask">
    <circle cx="100" cy="100" r="100" fill="white" />
  </mask>
</svg>
  • You also need to specify fill="white" for the drawing directives (such as the circle above).
  • Add a line to your CSS. Ask for a mask and include the URL of the Firefox mask file. Append the id of the mask element (avatar-mask in this example).
li {
  -webkit-mask-box-image: url(mask.svg) 0 stretch;
  /* Non-Webkit browsers */
  mask: url(mask-firefox.svg#avatar-mask);
}

Because Firefox masks won’t stretch with the element, we made a larger mask for the big version of the avatar. It makes the animation less elegant than the Webkit version, but it looks the same once the animation has completed.

CSS Animation

We started out thinking that we would need to use jQuery or Scriptaculous to animate the circles into the center when clicked. It turns out that CSS animations are supported in most modern browsers and make the code much easier.

CoffeeScript

Now that we know it, why not use CoffeeScript? We used it with jQuery to listen for a click on any of the images and add a CSS class. The class repositions the item to the center and makes it larger.

jQuery ->
  $('#circles li').click (event) ->
    $('#circles li').removeClass 'bigun'
    $(@).addClass 'bigun'

Animation

Until two days ago, we thought CSS animation was a complicated system that would take weeks to figure out.

Animating it only took two lines.

li {
  -webkit-transition-property: all;
  -webkit-transition-duration: 1s;
}

The easiest way is to specify all properties. When you add a CSS class to an element, all the new properties will animate. Otherwise, you can use a comma-delimited list of only the properties you want to animate: opacity,left,right, etc.

You can also add the same directives for Firefox and Opera:

li {
  /* Webkit */
  -webkit-transition-property: all;
  -webkit-transition-duration: 1s;

  /* Firefox */
  -moz-transition-property: all;
  -moz-transition-duration: 1s;

  /* Opera */
  -o-transition-property: all;
  -o-transition-duration: 1s;
}

See also

Other

Sass was instrumental in positioning the list items in a circular pattern. After calculating everything, we were able to change the radius of the hexagon and even the sizes of the circles until they fit.

li#circle1
  top: $cy - $hexagon_radius*0.866 - $radius
  left: $cx + $hexagon_radius/2 - $radius

Oh, and some photographs. We fitted a Nikon D7000 camera with a 50mm lens and hooked it to an inexpensive studio strobe and umbrella from AlienBees.

And of course, some face paint!