SEARCH
  Tutorials HTML and CSS Tutorials How to Slice Images into Tiles with jQuery and CSS3 Transitions

How to Slice Images into Tiles with jQuery and CSS3 Transitions

Did you ever wonder how those cool slideshow plugins split images into pieces and put them back together? This tutorial will get you a good understanding of the basic concept behind this effect as we build a simple jQuery plugin.

How to Slice Images into Tiles with jQuery and CSS3 Transitions

Check out the demo to see the tiles effect in action.

How to Slice Images into Tiles with jQuery and CSS3 Transitions

Project Setup

The first thing we need to do is to create a folder to hold our project. Let's name our plugin "sliced". Next, create three files,index.html,jquery.sliced.css and jquery.sliced.js. Grab this basic HTML template and copy/paste into index.html just so it's quicker to get started. Then download this image and put it in your folder. Now we're ready.

Let's create some initial markup:

<div class="sliced">
<img src="img01.jpg"/>
</div>

For the effect to work properly we need to set the dimensions of the container equal to the dimensions of the image.

.sliced {
position: relative;
width: 640px;
height: 400px;
}

Next, setup a basic jQuery plugin boilerplate:

;(function( $, window ) {
var _defaults = {
x : 2, // number of tiles in x axis
y : 2, // number of tiles in y axis
random : true, // animate tiles in random order
speed : 2000 // time to clear all tiles
};
$.fn.sliced = function( options ) {
var o = $.extend( {}, _defaults, options );
return this.each(function() {
var $container = $(this); // cache selector for best performance
// Code here
});
};
}( jQuery, window ));

How the Effect Works

The key to achieve this effect is background-position. Each tile is a div with the original image set as background and the background-position is calculated by getting each tiles' offset in relation to the parent container, using jQuery's .position(). Let's visualize it first:

You can fiddle with this CSS only demo to really understand how the effect works before abstracting the code.

Creating the Tiles

All the code from here on goes inside the return this loop.

First let's declare all the variables and elements that will be used to build the tiles.

var width = $container.width(),
height = $container.height(),
$img = $container.find('img'),
n_tiles = o.x * o.y, // total number of tiles
tiles = [], $tiles;

Next, we need to create all the wrappers for the tiles:

for ( var i = 0; i < n_tiles; i++ ) {
tiles.push('<div class="tile"/>');
}
$tiles = $( tiles.join('') );
// Hide original image and insert tiles in DOM
$img.hide().after( $tiles );

Now that the all tiles are in the DOM we can set the dimensions and the background:

// Set background
$tiles.css({
width: width / o.x,
height: height / o.y,
backgroundImage: 'url('+ $img.attr('src') +')'
});
// Adjust position
$tiles.each(function() {
var pos = $(this).position();
$(this).css( 'backgroundPosition', -pos.left +'px '+ -pos.top +'px' );
});

Finally float the tiles in jquery.tiles.css. This could be done in JavaScript but is better to keep our logic and styles separate:

.tile { float: left; }

At this point if you call the plugin on the container and inspect the markup with the browser you can see that with so little code we already have a prototype that works. Here's the plugin so far.

$('.sliced').sliced({ x:4, y:4 }); // Split image in 16 tiles

Animating the Tiles

Every single tile needs to start animating at a certain point in time. For example, to animate 4 tiles in 2 seconds each tile needs to be animated for half a second and the delay will increase by half a second every next tile until all tiles have cleared.

We need to loop all tiles somehow; the first thing that comes to mind is a for loop . We could write something like:

var tile, i;
for ( i = 0; i < n_tiles; i++ ) {
tile = $tiles.eq( i );
}

The above approach is alright but it doesn't provide a way to randomize the order in which the tiles are chosen to be animated. So yes, it works, but it's not ideal.

After much experimentation I found a simple way to get a range of numbers in random order:

/**
* range Get an array of numbers within a range
* @param min {number} Lowest number in array
* @param max {number} Highest number in array
* @param rand {bool} Shuffle array
* @return {array}
*/
function range( min, max, rand ) {
var arr = ( new Array( ++max - min ) )
.join('.').split('.')
.map(function( v,i ){ return min + i });
return rand
? arr.map(function( v ) { return [ Math.random(), v ] })
.sort().map(function( v ) { return v[ 1 ] })
: arr;
}

With this little function you can pass a minimum and maximum value and it will return an array with all numbers in the given range. If the rand flag is set to true it will shuffle the array. Try it:

console.log( range( 0, 5, true ) ) //=> [ 0,2,5,3,4,1 ]

Since this function doesn't depend on any jQuery code we should move it outside the plugin, at the same level where _defaults is declared.

Now that the tiles can be looped however we want let's use setTimeout to control the delays:

var tilesArr = range( 0, n_tiles, o.random ),
tileSpeed = o.speed / n_tiles; // time to clear a single tile
tilesArr.forEach(function( tile, i ) {
setTimeout(function(){
$tiles.eq( tile ).fadeTo( 'fast', 0 );
}, i * tileSpeed );
});

At this point if you try the plugin it will animate the tiles' opacity in random order as soon as the page is loaded but we want to have some control over this. This indicates the need for a public method. The simplest way to expose some code to the user is to create a custom event:

// Public method
$container.on( 'animate', function() {
tilesArr.forEach(function( tile, i ) {
setTimeout(function(){
$tiles.eq( tile ).fadeTo( 'fast', 0 );
}, i * tileSpeed );
});
});

Now when we call $('.slider').trigger('animate') the animation will begin.

Using CSS3 Transitions

So the basics are working, wonderful! but we want to have even more control. Instead of using fadeTo let's add a class to the tiles when they are animated, that way we can use CSS transitions with our new class. We'll use toggleClass() instead of addClass() so the effect can be toggled back and forth by just calling the animate method on a button for example.

$tiles.eq( tile ).toggleClass( 'tile-animated' );

Now, create a very basic opacity transition in CSS. For brevity only the unprefixed properties are used but you'll need to add the appropriate vendor prefixes in production.

.tile {
float: left;
opacity: 1;
transition: all .3s ease-in-out;
}
.tile-animated {
opacity: 0;
}

Last but not least, we need to prevent the CSS3 transitions from animating on page load. To fix this we can add a class to the body of the document while the contents are loading and then remove that class once the DOM has finished loading. This code doesn't depend on the plugin so let's add it before the plugin.

// Prevent css3 transitions on load
$('body').addClass('css3-preload');
$( window ).load(function(){ $('body').removeClass('css3-preload') });

Then cancel the transitions in CSS:

.css3-preload .sliced * {
transition: none !important;
}

Note: Run the plugin on document.ready() and not on window.load() otherwise the fix won't work properly.

Conclusion

Thanks to CSS3 transitions the effects that can be achieved with this approach are almost endless. A whole new effect can be created with just a single line of CSS, for example transform: rotateZ(360).

If you want to see what's really possible check out jquery.tiles, a jQuery plugin I recently released that creates a slideshow with a bunch of cool effects using the technique shown in this tutorial.

Note: Browser support is wide, we're only using CSS so it should degrade gracefully. Since the plugin takes advantage of some ECMAScript 5 methods such as map and forEach browsers that don't support these will need a polyfill like es5-shim or similar. Jquery's $.map and $.each could also be used but the native methods look cleaner and can be chained.

ABOUT THE AUTHOR

Onextrapixel OXP

Onextrapixel is a leading online magazine and resource site for designers and web developers. The Singapore based portal aims to collect, explore, as well as share useful tips, news, tutorials, tools and resources; on design, development and other inspiration. We hope that everyone will find something here that is useful and interesting.

subscribe to newsletter