Treehouse

Create an Exploding Logo with CSS3 and MooTools or jQuery

By on  
CSS Animation

When MooTools contributor and moo4q creator Ryan Florence first showed me his outstanding CSS animation post, I was floored.  His exploding text effect is an amazing example of the power of CSS3 and a dash of JavaScript.   I wanted to implement this effect on my new blog redesign but with a bit more pop, so I wrote some MooTools code to take static image and make it an animated, exploding masterpiece.  Let me show you how I did it, and as a bonus, I'm created a snippet of jQuery that accomplishes the same effect.

Ryan Florence's Animation Library

Ryan's CSS animation library, available with vanilla JavaScript, MooTools, or jQuery, and can only be described as a fucking work of art. His animation library is mobile-enabled, works a variety of A-grade browsers, and is very compact. Download and study Ryan's animation library before proceeding with this post.

Ryan's post also features an awesome demo and a few helpful functions. A few of these functions include:

// reset transforms to this
var zeros = {x:0, y:0, z:0};

// Implement animation methods on the element prototype
Element.implement({
	
	// Scatter elements all over the place
	scatter: function(){
		return this.translate({
			x: Number.random(-1000, 1000),
			y: Number.random(-1000, 1000),
			z: Number.random(-500, 500)
		}).rotate({
			x: Number.random(-720, 720),
			y: Number.random(-720, 720),
			z: Number.random(-720, 720)
		});
	},
	
	// Return them to their original state
	unscatter: function(){ 
		return this.translate(zeros).rotate(zeros);
	},
	
	//  Frighten the image!  AHHHHHHHH!
	frighten: function(d){
		this.setTransition('timing-function', 'ease-out').scatter();
		setTimeout(function(){ 
			this.setTransition('timing-function', 'ease-in-out').unscatter();
		}.bind(this), 500);
		return this;
	},
	
	// Zoooooom into me
	zoom: function(delay){
		var self = this;
		this.scale(0.01);
		setTimeout(function(){
			self.setTransition({
				property: 'transform',
				duration: '250ms',
				'timing-function': 'ease-out'
			}).scale(1.2);
			setTimeout(function(){
				self.setTransition('duration', '100ms').scale(1);
			}, 250)
		}, delay);
	},
	
	// Create a slider
	makeSlider: function(){
		var open = false,
			next = this.getNext(),
			height = next.getScrollSize().y,
			transition = {
				property: 'height',
				duration: '500ms',
				transition: 'ease-out'
			};
		next.setTransition(transition);
		this.addEvent('click', function(){
			next.setStyle('height', open ? 0 : height);
			open = !open;
		});
	},
	
	// Scatter, come back
	fromChaos: (function(x){
		var delay = 0;
		return function(){
			var element = this;
			//element.scatter();
			setTimeout(function(){
				element.setTransition({
					property: 'transform',
					duration: '500ms',
					'timing-function': 'ease-out'
				});
				setTimeout(function(){
					element.unscatter();
					element.addEvents({
						mouseenter: element.frighten.bind(element),
						touchstart: element.frighten.bind(element)
					});
				}, delay += x);
			}, x);
		}
	}())

});

Now let's jump on the exploding logo!

The HTML

The exploding element can be of any type, but for the purposes of this example, we'll use an A element with a background image:

<a href="/" id="homeLogo">David Walsh Blog</a>

Make sure the element you use is a block element, or styled to be block.

The CSS

The original element should be styled to size (width and height) with the background image that we'll use as the exploding image:

a#homeLogo	{ 
	width:300px; 
	height:233px; 
	text-indent:-3000px; 
	background:url(/wp-content/themes/2k11/images/homeLogo.png) 0 0 no-repeat; 
	display:block; 
	z-index:2; 
}
a#homeLogo span { 
	float:left;
	display:block;
	background-image:url(/wp-content/themes/2k11/images/homeLogo.png); 
	background-repeat:no-repeat;
}
.clear { clear:both; }

Remember to set the text-indent setting so that the link text will not display.  The explosion shards will be JavaScript-generated SPAN elements which are displayed as in block format.  Note that the SPAN has the same background image as the A element -- we'll simply modify the background position of the element to act as the piece of the logo that each SPAN represents.

The MooTools JavaScript

The first step is putting together a few variables we'll need to to calculate element dimensions:

// Get the proper CSS prefix from the page
var cssPrefix = false;
switch(Browser.name) { // Implement only for Chrome, Firefox, and Safari
	case "safari":
	case "chrome":
		cssPrefix = "webkit";
		break;
	case "firefox":
		cssPrefix = "moz";
		break;
}

if(cssPrefix) {
	
	// 300 x 233
	var cols = 10; // Desired columns
	var rows = 8; // Desired rows
	var totalWidth = 300; // Logo width
	var totalHeight = 233; // Logo height
	var singleWidth = Math.ceil(totalWidth / cols); // Shard width
	var singleHeight = Math.ceil(totalHeight / rows); // Shard height
	var shards = []; // Array of SPANs
	

You'll note that I've explicitly set the number of columns and rows I want.  You don't want the shards to be too large or too small, so feel free to experiment.  You could probably use another calculation to get column and row numbers, but I'll leave that up to you.

The next step is looping through each row and column, creating a new SPAN element for each shard.  The background position, width, and height of the SPAN will be calculated with the ... calculations ... we ... calculated ... above.

// Remove the text and background image from the logo
var logo = document.id("homeLogo").set("html","").setStyles({ backgroundImage: "none" });

// For every desired row
rows.times(function(rowIndex) {
	// For every desired column
	cols.times(function(colIndex) {
		// Create a SPAN element with the proper CSS settings
		// Width, height, browser-specific CSS
		var element = new Element("span",{
			style: "width:" + (singleWidth) + "px;height:" + (singleHeight) + "px;background-position:-" + (singleHeight * colIndex) + "px -" + (singleWidth * rowIndex) + "px;-" + cssPrefix + "-transition-property: -" + cssPrefix + "-transform; -" + cssPrefix + "-transition-duration: 200ms; -" + cssPrefix + "-transition-timing-function: ease-out; -" + cssPrefix + "-transform: translateX(0%) translateY(0%) translateZ(0px) rotateX(0deg) rotateY(0deg) rotate(0deg);"
		}).inject(logo);
		// Save it
		shards.push(element);
	});
	// Create a DIV clear for next row
	new Element("div",{ clear: "clear" }).inject(logo);
});

With the SPAN elements, you'll note that several CSS3 properties are being set to it, allowing the browser to do its magic.  Using CSS3 is much less resource-consuming within the browser than using JavaScript to do all of the animation.

The last step is calling the fromChaos method provided by Ryan Florence's CSS animation code to set into motion the madness!

// Chaos!
$$(shards).fromChaos(1000);

There you have it!  A completely automated method of exploding an image using CSS3 and MooTools JavaScript!

The jQuery JavaScript

Ryan also wrote the CSS animation code in jQuery so you can easily create a comparable effect with jQuery!

Number.random = function(min, max){
	return Math.floor(Math.random() * (max - min + 1) + min);
};

var zeros = {x:0, y:0, z:0};

jQuery.extend(jQuery.fn, {

	scatter: function(){
		return this.translate({
			x: Number.random(-1000, 1000),
			y: Number.random(-1000, 1000),
			z: Number.random(-500, 500)
		}).rotate({
			x: Number.random(-720, 720),
			y: Number.random(-720, 720),
			z: Number.random(-720, 720)
		});
	},

	unscatter: function(){ 
		return this.translate(zeros).rotate(zeros);
	},

	frighten: function(d){
		var self = this;
		this.setTransition('timing-function', 'ease-out').scatter();
		setTimeout(function(){
			self.setTransition('timing-function', 'ease-in-out').unscatter();
		}, 500);
		return this;
	},

	zoom: function(delay){
		var self = this;
		this.scale(0.01);
		setTimeout(function(){
			self.setTransition({
				property: 'transform',
				duration: '250ms',
				'timing-function': 'ease-out'
			}).scale(1.2);
			setTimeout(function(){
				self.setTransition('duration', '100ms').scale(1);
			}, 250)
		}, delay);
		return this;
	},

	makeSlider: function(){
		return this.each(function(){
			var $this = $(this),
				open = false,
				next = $this.next(),
				height = next.attr('scrollHeight'),
				transition = {
					property: 'height',
					duration: '500ms',
					transition: 'ease-out'
				};
			next.setTransition(transition);
			$this.bind('click', function(){
				next.css('height', open ? 0 : height);
				open = !open;
			});
		})
	},

	fromChaos: (function(){
		var delay = 0;
		return function(){
			return this.each(function(){
				var element = $(this);
				//element.scatter();
				setTimeout(function(){
					element.setTransition({
						property: 'transform',
						duration: '500ms',
						'timing-function': 'ease-out'
					});
					setTimeout(function(){
						element.unscatter();
						element.bind({
							mouseenter: jQuery.proxy(element.frighten, element),
							touchstart: jQuery.proxy(element.frighten, element)
						});
					}, delay += 100);
				}, 1000);
			})
		}
	}())

});


// When the DOM is ready...
$(document).ready(function() {
	
	// Get the proper CSS prefix
	var cssPrefix = false;
	if(jQuery.browser.webkit) {
		cssPrefix = "webkit";
	}
	else if(jQuery.browser.mozilla) {
		cssPrefix = "moz";
	}
	
	// If we support this browser
	if(cssPrefix) {
		// 300 x 233
		var cols = 10; // Desired columns
		var rows = 8; // Desired rows
		var totalWidth = 300; // Logo width
		var totalHeight = 233; // Logo height
		var singleWidth = Math.ceil(totalWidth / cols); // Shard width
		var singleHeight = Math.ceil(totalHeight / rows); // Shard height
		
		// Remove the text and background image from the logo
		var logo = jQuery("#homeLogo").css("backgroundImage","none").html("");
		
		// For every desired row
		for(x = 0; x < rows; x++) {
			var last;
			//For every desired column
			for(y = 0; y < cols; y++) {
				// Create a SPAN element with the proper CSS settings
				// Width, height, browser-specific CSS
				last = jQuery("<span />").attr("style","width:" + (singleWidth) + "px;height:" + (singleHeight) + "px;background-position:-" + (singleHeight * y) + "px -" + (singleWidth * x) + "px;-" + cssPrefix + "-transition-property: -" + cssPrefix + "-transform; -" + cssPrefix + "-transition-duration: 200ms; -" + cssPrefix + "-transition-timing-function: ease-out; -" + cssPrefix + "-transform: translateX(0%) translateY(0%) translateZ(0px) rotateX(0deg) rotateY(0deg) rotate(0deg);");
				// Insert into DOM
				logo.append(last);
			}
			// Create a DIV clear for row
			last.append(jQuery("<div />").addClass("clear"));
		}
		
		// Chaos!
		jQuery("#homeLogo span").fromChaos();
	}
});

Not as beautiful as the MooTools code, of course, but still effective!

And there you have it: CSS animations, JavaScript, and dynamic effects. My favorite part of this effect is how little code is involved. You get a lot of bang with your buck with this. Of course, using this effect everywhere would surely get groans so use it wisely!

ydkjs-1.png

Recent Features

Incredible Demos

  • jQuery Comment Preview

    I released a MooTools comment preview script yesterday and got numerous requests for a jQuery version. Ask and you shall receive! I'll use the exact same CSS and HTML as yesterday. The XHTML The CSS The jQuery JavaScript On the keypress and blur events, we validate and...

  • HTML5 Datalist

    One of the most used JavaScript widgets over the past decade has been the text box autocomplete widget.  Every JavaScript framework has their own autocomplete widget and many of them have become quite advanced.  Much like the placeholder attribute's introduction to markup, a frequently used...

Discussion

  1. Hasn’t your logo been exploding for about a month or so?

  2. This is really nice, adding it ;)

  3. looks really cool in webkit browsers, sadly awful in Firefox and Opera just mushes your face lol. Shame Firefox isn’t keeping up with Safai & GC these days

    • Right. Firefox will handle this much better in 4.0. As far as Opera and IE…we’ll have to see how well they do.

  4. Jay

    @Dan, try Firefox 4 beta, it should animate the same way that webkit browsers do.

    • I’ll give it a go :)

  5. wooow!! It’s really amazing!! As usual, thanks a lot David!

  6. Wow….that’s awesome David. xD

  7. Jay

    Is it possible to make the shards increase their z-index when they’re flying around? I notice that if you move your mouse along the shards at the top of the logo, if they travel down, they end up underneath the static shards of the lower section of the logo.

  8. Matthew F

    A friend pointed out that it is much like the exploding Canvas Video… except with a still image.

    http://craftymind.com/factory/html5video/CanvasVideo.html

  9. Thanks for this.
    I was checking for this script since you have launched the new design.

    Thanks again
    Av

  10. walter

    Awesome!! Thanks for sharing.

  11. This is awesome!

  12. Great technique. Degrades terribly in non-webkit browsers. I wonder how difficult a little browser ID routine would be to prevent less-capable browsers from participating.

  13. Rasha

    what is the difference between jQuery and mootools

  14. David

    Great effect!! But it’s possible make a good animations with only css (for example at http://www.polimalo.com header)

  15. Very funky! I like it!

  16. Giordhano

    this is really so cool !!
    Greetings from Peru !..

  17. stuart

    how do i activate the demo on a mobile browser?

    • You could probably add a touchstart event instead of mouseenter, and touchend instead of mouseleave.

  18. Silvio

    Great! Thank you very much!
    But, won’t work with Firefox 3.6.15 and IE 8.

  19. Gadriel

    i really like this exploding effect but i would really like to understand how it works since i can’t get it to work.. is there any chance you can share the complete code for this? i just can’t seem to get the MooTools to work. it’s the first time i try this method.. i can get the jQuery to work but i want to see how to work with MooTools. can you help?

    • The MooTools example page works, so feel free to take the code from there.

  20. ArKey

    Really amazing. For me not just an effect – I already have a use case ;-) Thanks for the article!

  21. Karlo

    I can get the jquery to work, but when I changed the image to a different size and changed the css width and height to the image size and changed the corresponding width and height in javascript, the image appears to be distorted. My image size is 391px(w) x 140px(h).

  22. Daniel

    I too am having problems with image distortion. My image in 860px x 140px and I’ve worked out that the correct col value is 31 and the row value is 5. My image still appears distorted though. The effect still works though.

  23. Juss

    David, is it difficult to make the animation start on load, like: ryanflorence.com/cssanimation/ and not only on mouse over? would you be so kind to help me adding that?

  24. How to modify the code to make the result as, the parts of a logo have already been scattered and they come back together after a certain delay, just like Ryan Florence’s homepage. Can you please write a code for that David? Would appreciate your help. Thanks!

  25. Hello there,

    I’m new to the world of javascript and its libraries. I am a graphic designer, I love this effect and I am trying to implement it in one of my projects. I have isolated it to its bare bones and I cannot for the life of me get it to work (MooTools or jQuery versions). All I can narrow it down to is I am not prefixing my css properly. Could someone please post here how they prefixed there CSS (if at all)

    Thanks

  26. Hello again,

    Found your effect posted on codepen.io which helped me understand it a lot better and managed to get it working. I have been inspecting element on ryanflorence.com/cssanimation/ to see if I can get the animation to start scattered on page load. Not managed it yet, if anyone has had any joy getting it to work on page load please do share.

    Cheers!

  27. Why does this seem to not work correctly when the width is set differently? It cuts off part of my logo because it does not fit within the “300px” in the css or the “totalWidth = 300;” in the javascript and it cuts my logo into pieces and scrambles them up. Am I missing something I need to change the size?

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!