Animated Knockout Letters

Published by Chris Coyier

I was watching some commercial the other day and they had these letters fly in over a black screen and reveal an image beneath them. It was a pretty cool looking effect and it reminded me how WebKit has that cool -webkit-background-clip property which allows you to show background through text. So I set off on trying to see if I could do something like that. You can, but there are some interested stumbling points...

This is the final product:

View Demo   Download Files

This will only look right in WebKit browsers (Safari, Mobile Safari, Chrome). The download won't have the fancy font (see below).

Knockout Basics

There isn't much to it:

.display-sweet-image-behind {
   /* fallback color */
   color: white; 

   /* overrides color with nothingness */
   -webkit-text-fill-color: transparent;
   
   /* remember non WebKit browsers will see all of this EXCEPT the text */
   background: url(images/zombies-making-out.jpg) no-repeat; 

   /* the magic */
   -webkit-background-clip: text;

   font: bold 100px Impact, Sans-Serif;
}
<div class="display-sweet-image-behind">Nom nom nom</div>

Moving the letters around

Turns out it's totally cool if you wrap the letters in spans, like this:

<div class="display-sweet-image-behind">
<span>N</span>
<span>o</span>
<span>m</span> 
nom nom
</div>

The letters will still knock out and all is well. It's not just spans, it's that you are wrapping them in an inline level element. If you use inline-block, or block, or anything else, the knockout stops working. =(

So because the span's work, you'd think we could move them around with relative positioning. Mega fun!

.display-sweet-image-behind span {
   position: relative;
}
.display-sweet-image-behind span:nth-child(1) {
   top: -20px;
   left: -20px;
}

The above code would move the first letter up and to the left. But no! Fail! Something about the positioning makes the knockout fail. Double =( =(

As a test, I tried out using margins on those spans, and that IS allowed, so hallelujah, that'll do for fun animation time. Although because these are inline elements, we only get left and right margin, top and bottom have no effect.

.display-sweet-image-behind span:nth-child(1) {
   margin-left: -100px; /* remember this will yank all the letters over this far */
}

Lettering.js

So yeah, do you have to wrap every single letter in a span manually? Nah, that's for suckers. Lettering.js is a jQuery plugin that does exactly that. Literally. You call it on a set of elements and it'll wrap every single letter in a span with a class name referencing its position.

$(".display-sweet-image-behind").lettering();

Super simple, super cool. I should have thought of it.

Animate it

The grand goal here is to have the letters animate themselves into place. So here's our markup for the final example:

<div id="poster">
	<h1>Red Sonja</h1>
	<p>Coming 2011</p>
</div>

We'll get all the letters into spans with Lettering.js with one line of JavaScript:

$("#poster h1, #poster p").lettering();

For the knockout letters, we'll give them a transition, so that any properties they have that change will animate. Only WebKit browsers will have the knockout, but we might as well have the letters behave the same.

#poster h1 span { 
	-webkit-transition: all 2.5s;
	-moz-transition: all 2.5s;
	-o-transition: all 2.5s;
}

The classes that Lettering.js applies are char1, char2, etc. So we'll use those classes to apply big margins and kinda kick the letters off the page.

#poster h1 span.char1 { margin-left: -1450px; } 
#poster h1 span.char2 { margin-left: 200px; }
#poster h1 span.char3 { margin-left: 200px; }
#poster h1 span.char5 { margin-left: 1450px; }
#poster h1 span.char6 { margin-left: 200px; }
#poster h1 span.char7 { margin-left: 200px; }
#poster h1 span.char8 { margin-left: 200px; }
#poster h1 span.char9 { margin-left: 200px; }

We're going to go for a short delay before the animation starts. This will give Lettering.js a chance to do it's thing and for the fancy font to load. Also, this is kind of an "experience" effect where waiting a second just makes it more suspenseful.

What we'll do is just wait one second, and then apply a class name to the html element.

setTimeout(function() {$('html').addClass("step-one");}, 1000);

We'll use that class name to override all those margins we set, resetting them back to zero and the letters back in their natural position.

.step-one #poster h1 span { margin: 0; }

The other letters in the demo also use the Lettering.js spans and do their animation via CSS transitions and delayed applied class names, only they aren't knockouts. Because they aren't knockouts (just white type), we can use position: relative.

Let's randomly set their position, then have "Step Two" of the animation happen a few seconds latter, where a class name will kick them back into place.

$("#poster p span").each(function() {  
   $(this).css({ 
      top: -(Math.floor(Math.random()*1001)+1500), 
      left: Math.floor(Math.random()*1001)-500,  
   }); 
});

setTimeout(function() {$('html').addClass("step-two");}, 3000); 

This time we'll have to use !important rules because jQuery will apply the CSS values with inline styling, and !important is needed to override that.

.step-two #poster p span { 
  top: 0 !important; 
  left: 0 !important; 
}

Wrapup

In case you are curious, the font in use there is Newcomen available on Typekit.

Even though margin could only handle the left and right margin, the up and down animation I got was from animating the top padding on the parent element. Tricksy.

Oh, and I have no idea if they are really going to remake Red Sonja or not. Seems like a rumor.

View Demo   Download Files