Gameify Everything: Merit Badges For LifeSubverting The Cascade: Using !important In CSS31 Essential Browser Keyboard Shortcutsfloat: center With ColumnsWhat Does Two Years of Not Drinking Look Like?Music to Code ByA Cycle Of Continual ImprovementWeb Development Sandbox Survey 2012An Introduction to SVG15 More Filler Text Generators For Web Page MockupsMass Effect: The Definitive Space Opera of Our TimeUse CSS to Auto-Generate Image ThumbnailsCSS Masks: Radial VignettesEight Things That Will Seem Weird In 100 YearsCross-browser Image BlurHTML5 Calendar With CSS3 & MicrodataPhotoShop Batch Processing: Generating Image Thumbnails via Automated Crop & ResizeCrafting Optimal Type Sizes For Web PagesWhat Does 20 Years Of Reading Looking Like? (Chapter One)CSS3 Neon SignBook Review: HTML & CSSContent Tabs With :targetBuild A Responsive Web ResumeEasy Custom Controls For HTML5 Video With JQuerySAIT Polytechnic NMPD Showcase – April 16 and 17, 2012CSS3 2D Transformations: rotateCSS Sprites for the Modern Era: Refined, Animated, and SemanticBook Review: Explorations In TypographyIntroduction to HTML Image Filtration MethodsCSS3 Animated Image Galleries: Cross-fade and Slideshow EffectsColor in CSSHealth Tips For Web DevelopersMy New Geek HeroesA Brief Exegesis On The Political Class Conflicts Of The Alien Movie SeriesCSS3 Timer With Keyframe AnimationBig Data: Information Sources For Forms & DatabasesThe Ultimate TV Series CrossoverClassic Typography Effects in CSS: Initial Cap, Drop CapMarking Your Territory: Using Address Microformats With Search EnginesText Clipping Masks With SVGHobbiton TourEnhancing Imagemaps With CSS3 PopupsSlash PNG File SizesCSS clipCaptioning of Portfolio Images With GETDevelop A Polaroid Photograph With CSS3 TransitionsApplying Curved Dropshadows To HTML ElementsCycling the Myra Canyon Trestles / Kettle Valley RailwayCSS3 Lifted Drop ShadowSimple CSS Masks: Rounded Corners On ImagesCreate Rich Web Typography With CSS3 & OpenType: Discretionary Ligatures & True Small CapsSite Optimisation: Ultimate Image Compression ToolsCountdown to SkynetJQuery Window Smooth Scroll Effect

As site information density grows, designers are challenged to create new ways of visualizing and navigating data. One way of providing an overview of site content is to display a grid of random thumbnail images, with each image corresponding to a site page or article. When used in the past, this kind of mosaic has been static, and often fails to communicate the dynamic, complex nature of a modern site.

For the next redesign of this blog I wanted to create a 404 page with an animated mosaic of article image thumbnails to communicate the themes of the site. I wanted the animation to appear somewhat random, with each cell in the mosaic flicking to a new image independently of the others.

Gaining The Images

The first issue is getting all the images. Obviously, you need a large “stock” of thumbnails to choose your random images from: in this example the displayed set will total 54 pictures. You could gain the thumbnails from any source you wished: a folder of images that have been batch-processed with PhotoShop*; drawing content from a photo service such as flickr or 500px using an API, or using a to randomly draw images from a database. I’ve covered most of those techniques in past articles, so how you gain the images won’t be our concern here: in the code below, I’ve assumed a database call.

We need to arrange the images in groups, with a stack of pictures in each group. The images will be placed inside <span> elements, all contained inside an HTML5 <header>.

The PHP

Within the loop that prints the results of the query to the web page I’ve used a modulo comparison to place three images in each <span> before it is closed. I’ve also generated a random number between 1 and 5, and appended this number to a class attribute for each span: writing CSS that references these classes later will allow us to create the impression of random animation.

<header>
<?php $mosaic = $db->store_result();
$counter = 0;
while($result = $mosaic->fetch_assoc()) { 
	$random = rand (1 , 5);
	if (($counter % 3) == 0) {
		echo '<span class="mosaic'.$random.'">';
	}  
	echo '<img src='.$result['thumbnail'].' alt="'.$result['title'].'">';
	if (($counter % 3) == 2) {
		echo '</span>';
	}
	$counter++;
}
$mosaic->free(); ?>
</header>

The HTML result

Provided with a pool of images, the would produce an HTML result that looked something like the following:

<header>
	<span class="mosaic2">
		<img src="los-angeles.jpg" alt="Music to Code By">
		<img src="checklist.jpg" alt="A Site Maintenance Checklist">
		<img src="bill-paxton-aliens.jpg" alt="Alien Political Class Conflicts">
	</span>
	<span class="mosaic3">
		<img src="ligature-small.png" alt="CSS3 Ligatures and Kerning Pairs">
		<img src="css3-rotate.jpg" alt="CSS3 2D Transformations: rotate">
		<img src="carolina-de-bartolo.jpg" alt="Explorations In Typography">
	</span>
	...
</header>

The CSS

We’ll make the <header> 768px wide, which is a useful number for equal division: each <span> will be 128px square, which allows us to fit exactly six <span> elements per row. (If you wanted to make the mosaic responsive, each <span> would be 16.66% of the width of the <header> container).

header {
	width: 768px;
	padding: 0;
} 
header span {
	display: inline-block;
	width: 128px;
	height: 128px;
	position: relative;
	margin: 0;
}
header span img {
	width: 128px;
	height: 128px;
	position: absolute;
}

Adding Animation

Now the CSS (displayed here without vendor prefixes to save on space). The entire animation will depend on just one keyframe sequence:

@keyframes mosaic {
	0% { opacity: 1; }
	25% { opacity: 1; }
	33% { opacity: 0; }
	100% { opacity: 0; }
}

It’s a very simple set of directives: keep the affected element solid for ¼ of the total length of the animation, fade it out over 8% of the time, and then keep it transparent until the end of the animation sequence.

The only images to be animated will be the last image and the one in the middle of each stack: the last image will appear on “top” due to absolute positioning, and we don’t wish to alter the first image, as there is nothing underneath it.

To start, we’ll animate the two images at the top of each stack the same way by using a specialized attribute and nth-child selector:

span[class^="mosaic"] img:nth-child(3),
span[class^="mosaic"] img:nth-child(2) {
	animation-name: mosaic;
	animation-duration: 12s;
	animation-iteration-count: infinite;
}

As a default setup, this isn’t a great start: both of the top images in every <span> will fade out at exactly the same time. We’ll add variety to this by addressing the images via the randomly generated number at the end of each class; a few examples should be enough to impart the general idea:

.mosaic1 img:nth-child(3) { 
	animation-direction: alternate;
}
.mosaic1 img:nth-child(2) {
	animation-duration: 8s;
	animation-delay: 4s;
}
.mosaic2 img:nth-child(3) {
	animation-delay: 2s;
}
.mosaic2 img:nth-child(2) {
	animation-delay: 6s;
}
.mosaic3 img {
	animation-duration: 21s;
}
.mosaic3 img:nth-child(3) {
	animation-delay: 7s;
}
.mosaic3 img:nth-child(2) {
	animation-delay: 14s;
}

As you can see, each class is given a slight variation: starting a little later, reversing direction at the end of the animation sequence, and so on. Combined together, this gives the randomized result you see at the top of this article.

Possible Improvements

There are, as always, some improvements that could be made: in theory, an image sprite could be used as an alternative to loading individual images, reducing the number of HTTP requests to 1. The drawback is CSS that cannot currently animate background images with the same flexibility as it can image elements, and the CSS to position the images would be burdensome. I’ve shown how to make “true” image sprites in the past by using the clip property, which would allow more options for animation, but still produce an awful lot of CSS.

There are many more possibilities, and I look forward to seeing your variations on the technique.

*Technically there’s no absolute requirement for all the thumbnail images to be the same size. So long as you could guarantee that they were all at least as tall as they were wide, you could simply make the images the same width with CSS and clip them by using overflow: hidden on the span elements.

Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.