before and after

A client came to us recently and asked us to duplicate the functionality seen on this nytimes article.

One of our talented designers, Jacks, came up with an updated design and passed it along to me for development.

Screenshot

At first glance, it seems like something that would be a little tricky to re-create without flash.  As the resident flash guy, I’ve been getting a lot of pressure from the internet community at large to start flexing my Javascript muscles, so due to its lack of special fonts and uncomplicated layout, I set out to create it using HTML, CSS and jQuery UI and ended up with this little bit of JS, CSS, and HTML – before and after.


Here’s how I created it.

First, I created the layout in HTML and CSS, that way I got the base styles and layout ready before introducing the JS and any additional styles they require.

After a little trial and error, My HTML ended up looking like this:

<div id="beforeAfter">
	<div id="canvasContainer">
		<div id="canvas">
			<div id="before"><img src="img1.jpg" alt="Before" height="413" width="718" /></div>
			<div id="after"></div>
			<div id="sliderContainer">
				<div id="slider"></div>
			</div>
		</div>
	</div>
</div>

My CSS ended up looking like this:

body { background:#999; }
#beforeAfter { background:#656565 url(bg.gif) repeat-x; padding:4px 0 19px; width:730px; }
#canvasContainer { height:417px; margin-left:4px; width:722px; }
#canvas { background:#fff; display:none; height:417px; overflow:hidden; position:relative; width:722px; }
#before { position:absolute; right:2px; top:2px; }
#after { background:url(img2.jpg) top right no-repeat; border-left:3px solid #fff; height:413px; position:absolute; right:2px; top:2px; width:722px; z-index:2; }
#sliderContainer { bottom:-32px; height:31px; left:2px; overflow:hidden; position:absolute; width:718px; z-index:3;}
.ui-slider { left:-6px; position:absolute; width:663px; z-index:3; }
.ui-slider-handle { background:url(handle.png) no-repeat; cursor:pointer; display:block; height:31px; position:absolute; width:67px; }

The beforeAfter div is responsible for the background and dimensions.
The canvasContainer div creates the height and width of the canvas so that when I show the images, the height remains the same.
The canvas div is the container of my before and after images.
The before div is the image before and the after div is my image after.

The slideContainer div matches the width of the images and has the ui-slider inside it. Since my handle has a drop shadow, I needed to offset the slider by the extra pixels from the handle’s shadow. I added overflow:hidden to the sliderContainer so that when the slider handle gets to the edge of the slider, it lines up with the edge. I also set up the ui-slider’s width to be the total width of the images minus the width of the handle.


Next, I went to work on the Javascript.  I grabbed jQuery 1.4 from the google jsapi and a custom jQuery ui download of just the slider widget.

After more trial and error, I came up with this JS (read through the comments for an explanation):

// wait for everything to be loaded before doing anything
$(document).ready(function(){
	// set the max width var because it's used in several places
	var max=722;
	// set a var to reference the after div
	var after = $('#after');
	// initialize the slider
	$("#slider").slider({ max:max, min:0,
		slide:function(e, ui){
			// when the slider slides, set the width of the image based on the max width
			after.width(max - ui.value);
			// if it is at the max, hide it. If it is not and it's not visible, show it.
			if(ui.value===max){
				after.hide();
			} else if(!after.is(':visible')) {
				after.show();
			}
		}
	});
	// create an image in the dom to load the after image so we don't display the widget until the image is there
	var img = new Image();
	img.onload=function(){
		// show it all with a couple of effects
		$('#canvas').delay(500).fadeIn('normal');
		$('#sliderContainer').delay(800).animate({bottom:0});
		img=null;
	}
	// set the image source to kick this whole thing off
	img.src='img2.jpg';
});

For the animation, I tried to make it simple and clean. One of my biggest pet peeves with a lot of the JS/HTML/CSS work that is coming out these days is that it’s not seamless.  There is a skip, there is a blink, there is a jump when the animation starts or ends.  It just doesn’t produce the quality of animation/motion graphics that you can get from using flash.  To avoid this, I chose the simpler animation effects and added a bit of delay so that the user can actually see what’s happening.

Grab the source here

Comments

    Jacks |

    Looks fantastic! Love the little animation that pops up the slider when it loads too, great touch.