Perform a Split Reveal With CSS

by on 10th January 2013 with No Comments

screenshot

CSS can pull of a lot of really great image tricks: size manipulation, desaturation, even blur. One limitation that we run into though is that you can’t really slice an image into multiple parts. For instance, if you wanted to cut a photo in half and animate the separation, you couldn’t really do it with pure CSS. Could you?

Today we’re going to code up a work around that allows us to achieve this very trick without an ounce of JavaScript or extra files. Let’s see how it works.

Sneak Peak: Launch Demo

Hat Tip to Kyle Foster

Recently, developer Kyle Foster created a nice little jQuery plugin with an effect that I really like (shown here). When the user hovers over an image, it splits in half horizontally and separates to reveal a hidden message. Here’s a screenshot of his demo:

screenshot

As you can see, it’s a great little piece of UI. As I always do when I come across something like this, I began thinking about how to achieve something similar with only CSS. The problem is that CSS doesn’t really let you split an image like that. I knew multiple background images held the solution, but I didn’t want to have to embed two separate images for every photo that used this effect. Fortunately, with a little ingenuity, we can pull this off with a single image. Read on to see how.

Step 1: Format Your Image

CSS allows us to apply multiple background images to a single element. Interestingly enough, we can use this functionality to apply the same background image multiple times, positioning each instance in a unique way. With this information in mind, a solution to our problem presents itself.

The trick to pulling this off is to split your image in half in Photoshop before you embed it. Check out the example below to see what I mean. It looks a little weird, but it gives us the ability to do exactly what we need.

screenshot

The workflow to do this goes like this (feel free to tweak the dimensions):

  • Create an image that’s 300px wide by 300px tall
  • Cut the image right across the middle and place the two halves on different layers.
  • Go to Canvas Size in Photoshop and increase the canvas to 300px by 375px.
  • Place the bottom half of the photo at the top of the canvas.
  • Place the top half of the photo at the bottom of the canvas.

This sounds like a pain, but once you wrap your mind around how it works, it goes really quickly. You can even record an action that’ll do this for you in a few seconds. Basically, what we’ve done is make it possible for one file to serve as both the top and bottom of our image. It can perform this function because it tiles nicely: when two are bumped up against each other, the image seamlessly flows from one to the other. Don’t worry about the giant white stripe in the middle, that will simply be hidden.

Step 2. The Markup

Now that we have a good idea for how this is going to work, let’s mark it up. Remember that we want a caption to be revealed, so we’ll place a paragraph tag inside of a div.

<div class="reveal plug">
  <p>lorem</p>
</div>

This div has two classes: reveal and plug. The “reveal” class is where we’ll place a lot of the repeatable code that we’ll want to carry out across all similar elements. The “plug” class will contain the specific code that relates to the plug photo shown above.

Step 3. Starter CSS

To hit the ground running, here’s the basic CSS that I’ll be using in the demo. The part that really matters is the “reveal” code, which sets the size of our container.

* {
  margin: 0;
  padding: 0;
}

body {
  background: #222;
}

.reveal {
  width: 300px;
  height: 300px;
  margin: 50px;
  float: left;
}

Step 4. Multiple Background CSS

Now, remember that our starting image was 300px by 300px, which is the size that we made our container. This means that we can fill that container with image area without letting any of the white bar on our jpg show.

To do this, we’ll set three backgrounds on our element, each separated by a comma. The first two are the same plug image and the third is a solid color. On the one hand, we’re sort of using multiple images, but the browser only has to load in one file so we’re keeping our http requests lower than if we had used two distinct images (it’s basically just like using image sprites).

.plug {
  background: url(plug.jpg) 0px 150px, url(plug.jpg) 0px -225px, #f6d9ad;
  background-repeat: no-repeat;
}

The numbers after each of the backgrounds represent positioning values. The first number is horizontal offset and the second is vertical offset. We offset our first image a positive 150px and the second a negative 225px. Note that 150+225 equals 375, which is the full height of our jpg.

screenshot

Also note that it’s very important that you set the backgrounds to no-repeat, otherwise, this won’t work at all.

Step 5. Transition

When we hover over the element, we’re going to slide the two instances of our photo away from each other to reveal a caption. We want to make sure this happens smoothly, so be sure to add in a transition.

.plug {
  background: url(plug.jpg) 0px 150px, url(plug.jpg) 0px -225px, #f6d9ad;
  background-repeat: no-repeat;
  -webkit-transition: background-position 0.3s ease;
  -moz-transition: background-position 0.3s ease;
  -o-transition: background-position 0.3s ease;
  -ms-transition: background-position 0.3s ease;
  transition: background-position 0.3s ease;
}

Step 5. Hover Styles

At this point, all of the hard stuff is finished. All we have to do is move each instance of the image outward from the center when the user hovers over the element. We do this by tweaking those positioning value like so:

.plug:hover {
  background: url(plug.jpg) 0px 210px, url(plug.jpg) 0px -285px, #f6d9ad;
  background-repeat: no-repeat;
}

Now when a hover event occurs, the first instance of the image will slide down by 60px and the second instance will slide up by 60px. Here’s what this looks like:

screenshot

Step 6. The Caption

With that, our seemingly impossible effect actually becomes pretty dang simple! Don’t forget though, we have that pesky caption to work with now. Given that we’re embedding our photo with CSS background images, the text will actually appear on top of the background, which isn’t want we want at all. With a little extra markup, we could create an extra div and keep the text elsewhere, which works just fine.

However, we can keep our markup to a minimum and simply fake it if we manipulate the opacity of the text. Using this method, we’ll simply make the caption invisible until the user hovers over the image, then fade it into view. Here’s the chunk of CSS that we’ll need to do all this.

.reveal p {
  font: 45px/300px Helvetica, Arial, sans-serif;
  text-align: center;
  -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
  filter: alpha(opacity=0);
  opacity: 0;

  -webkit-transition: all 0.3s ease;
  -moz-transition: all 0.3s ease;
  -o-transition: all 0.3s ease;
  -ms-transition: all 0.3s ease;
  transition: all 0.3s ease;
}

.reveal:hover p {
  -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
  filter: alpha(opacity=100);
  opacity: 1;
  cursor: pointer;
}
screenshot

Step 7. Lather, Rinse, Repeat

That’s pretty much all there is to it. We’ve set up a system that can easily be expanded across various elements. Here we’ll quickly apply this to three items.

<div class="reveal curve">
  <p>lorem</p>
</div>

<div class="reveal plug">
  <p>ipsum</p>
</div>

<div class="reveal paper">
  <p>doller</p>
</div>

Now we can use similar CSS across each version, applying the same positioning values across the board while mixing up the background images.

* {
  margin: 0;
  padding: 0;
}

body {
  background: #222;
}

.reveal {
  width: 300px;
  height: 300px;
  margin: 50px;
  float: left;
}

.curve {
  background: url(curve.jpg) 0px 150px, url(curve.jpg) 0px -225px, #f6d9ad;
  background-repeat: no-repeat;
  -webkit-transition: background-position 0.3s ease;
  -moz-transition: background-position 0.3s ease;
  -o-transition: background-position 0.3s ease;
  -ms-transition: background-position 0.3s ease;
  transition: background-position 0.3s ease;
}

.curve:hover {
  background: url(curve.jpg) 0px 210px, url(curve.jpg) 0px -285px, #f6d9ad;
  background-repeat: no-repeat;
}

.plug {
  background: url(plug.jpg) 0px 150px, url(plug.jpg) 0px -225px, #a79168;
  background-repeat: no-repeat;
  -webkit-transition: background-position 0.3s ease;
  -moz-transition: background-position 0.3s ease;
  -o-transition: background-position 0.3s ease;
  -ms-transition: background-position 0.3s ease;
  transition: background-position 0.3s ease;
}

.plug:hover {
  background: url(plug.jpg) 0px 210px, url(plug.jpg) 0px -285px, #a79168;
  background-repeat: no-repeat;
}

.paper {
  background: url(paper.jpg) 0px 150px, url(paper.jpg) 0px -225px, #c9bfaa;
  background-repeat: no-repeat;
  -webkit-transition: background-position 0.3s ease;
  -moz-transition: background-position 0.3s ease;
  -o-transition: background-position 0.3s ease;
  -ms-transition: background-position 0.3s ease;
  transition: background-position 0.3s ease;
}

.paper:hover {
  background: url(paper.jpg) 0px 210px, url(paper.jpg) 0px -285px, #c9bfaa;
  background-repeat: no-repeat;
}

.reveal p {
  font: 45px/300px Helvetica, Arial, sans-serif;
  text-align: center;
  -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
  filter: alpha(opacity=0);
  opacity: 0;

  -webkit-transition: all 0.3s ease;
  -moz-transition: all 0.3s ease;
  -o-transition: all 0.3s ease;
  -ms-transition: all 0.3s ease;
  transition: all 0.3s ease;
}

.reveal:hover p {
  -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
  filter: alpha(opacity=100);
  opacity: 1;
  cursor: pointer;
}

Take a Look!

Our work is finished, now it’s time to sit back and admire what we built. Check it out by following the link below. Once you’ve given it a look, leave a comment below and let me know what you think.

Demo: click here to launch

screenshot

Comments & Discussion

Subscribe

Membership
About the Author