Exploding Blocks with CSS and Javascript

August 21, 2013 at 3:53 pm By
Download Demo

Ever wanted to make your HTML blocks explode when it’s clicked or touched? Well I did, and today I’m going to show you how I went about doing it. We’re mainly using CSS, but that’s all being run by Javascript just for ease. Read on to learn how it was done!

So I was kind of inspired by this little gif I saw that had a block explode when it was clicked, and I thought that was really cool. So I started thinking, how would you do that with CSS and maybe some Javascript? Well I figured it out using a bit of math, and it functions as a pretty cool delete button.

Clipping

So I started with my div. It was just a regular old blue div with some content in it. I added some basic text styles too.

BLOCK

<div class="clipped-box">
		
	<div class="content">
		<h1>CLICK TO DELETE ME</h1>
		<p>I'll explode into pieces!</p>
	</div>
				
</div>

I figured I wanted this to fragment into a bunch of pieces and the best way to do that was to use the CSS clip property. So instead of me cutting each block out individually and having to write 25 different divs, I had a little function calculate it for me. Using the width and height of the first div and a specific number (say, 5, meaning 5 divs across and down for a total of 25 divs), I populated the div with a bunch of clipped divs that contained the same content.

$(document).ready(function() {
		
	// Generate the clips. In this case I'm using 5 (or 25 pieces)
	(genClips = function() {
		
		// For easy use
		$t = $('.clipped-box');
		
		// Like I said, we're using 5!
		var amount = 5;
		
		// Get the width of each clipped rectangle.
		var width = $t.width() / amount;
		var height = $t.height() / amount;
		
		// The total is the square of the amount
		var totalSquares = Math.pow(amount, 2);
		
		// The HTML of the content
		var html = $t.find('.content').html();
		
		var y = 0;
		
		for(var z = 0; z <= (amount*width); z = z+width) { 
		
			$('
'+html+'
').appendTo($t); if(z === (amount*width)-width) { y = y + height; z = -width; } if(y === (amount*height)) { z = 9999999; } } })();

The key line here is where we clipped the content. If you didn’t know already, clip can cut a div along any point making the rest of the div invisible. So lets say you did this:

clip: rect(0px 60px 60px 0px);

That gives you a block that is cut out starting at (0px, 0px) and going across 60px and down 60px.

CLIPPED

Random Numbers

The angle and skew of the blocks as they fly off into the distance is based on a random number generator function. I used quite a few random numbers so I made it into a function:


// A quick random function for selecting random numbers
function rand(min, max) {
	return Math.floor(Math.random() * (max - min + 1)) + min;		
}

Explosion!

So next up is the explosion. To accomplish this I used equations of motion. I’m sure a lot of you are familiar with them, but for those that aren’t, they allow you to model a particle’s movement. More specifically, I modelled each clipped div as a projectile going through a projectile arc.

For that reason we’re using an angle of projection, and I’ve stuck this all in an setInterval() so that the divs will actually in an arc fashion. Since the interval is being fired every 30ms and I’m increasing the time passed by 300ms, the timing is a bit off and I had to use quite large numbers for the speed (as in, 140m/s projection speed), but it made the animation more believable, so I just ran with it.

So how does an arc work? Well an object is projected at a speed, at an angle, it reaches its peak or apex and slows down as it reaches it, then it gathers up speed again and begins its fall to zero. So to begin we have to define some random numbers so each particle flies off differently.

We also have to change the colour of each particle so it appears separate, and set the direction it flies in on the x axis (i.e. to the left, right or not at all)

// On click
$('.clipped-box div').on('click', function() {
	
	if(clicked === false) {
			
		// Has been clicked, so ignore any other click until this is reset.
		clicked = true;
			
		// Hide the content box so we can see the particles fly.
		$('.clipped-box .content').css({'display' : 'none'});	
	
		// Apply to each clipped-box div.
		$('.clipped-box div:not(.content)').each(function() {
				
			// So the speed is a random speed between 90m/s and 120m/s. I know that seems like a lot
			// But otherwise it seems too slow. That's due to how I handled the timeout.
			var v = rand(120, 90),
				angle = rand(80, 89), // The angle of projection is a number between 80 and 89 degrees.
				theta = (angle * Math.PI) / 180, // Theta is the angle in radians
				g = -9.8; // And gravity is -9.8. If you live on another planet feel free to change
					
			// $(this) as self
			var self = $(this);

			// time is initially zero, also set some random variables. It's higher than the total time for the projectile motion
			// because we want the squares to go off screen. 
			var t = 0,
				z, r, nx, ny,
				totalt =  15;
				
			// The direction can either be left (1), right (-1) or center (0). This is the horizontal direction.
			var negate = [1, -1, 0],
				direction = negate[ Math.floor(Math.random() * negate.length) ];
				
			// Some random numbers for altering the shapes position
			var randDeg = rand(-5, 10), 
				randScale = rand(0.9, 1.1),
				randDeg2 = rand(30, 5);
			
			// Because box shadows are a bit laggy (by a bit I mean 'box shadows will not work on individual clipped divs at all') 
			// we're altering the background colour slightly manually, in order to give the divs differentiation when they are
			// hovering around in the air.
			var color = $(this).css('backgroundColor').split('rgb(')[1].split(')')[0].split(', '),
				colorR = rand(-20, 20),  // You might want to alter these manually if you change the color
				colorGB = rand(-20, 20),  // To get the right consistency.
				newColor = 'rgb('+(parseFloat(color[0])+colorR)+', '+(parseFloat(color[1])+colorGB)+', '+(parseFloat(color[2])+colorGB)+')';
		
				
			// And apply those
			$(this).css({
				'transform' : 'scale('+randScale+') skew('+randDeg+'deg) rotateZ('+randDeg2+'deg)', 
				'background' : newColor
			});

With me so far? It’s a good time to mention that I also applied some transitions to each particle div so the background colour change and transform change were fluid.


.clipped-box div {
	position: absolute;
	top: auto;
	left: 0;
	background: #4F9CC7;
	-webkit-transition: -webkit-transform 1.4s ease-in, background 0.3s ease-in;
	transition: transform 1.4s ease-in, background 0.3s ease-in;
}

Setting up the Interval

Calculating the position of the div as it flies through the metaphorical air is the first step. To do that we’re using s = ut + 0.5at2. That calculates the displacement based on acceleration and time. We’re using a total time that is greater than the time of the projectile arc so that the divs will go off screen.

We also need to calculate vertical and horizontal speed differently, so a little trigonometry is required. We just need to reposition the divs based on that information after that. When the first div finishes its arc we use it as a point to reset the animation. Since the time means they go quite far off screen it’s safe to assume that all divs have gone off screen by the time the first div finishes.

			z = setInterval(function() { 	
					
				// Horizontal speed is constant (no wind resistance on the internet)
				var ux = ( Math.cos(theta) * v ) * direction;
				
				// Vertical speed decreases as time increases before reaching 0 at its peak
				var uy = ( Math.sin(theta) * v ) - ( (-g) * t);
				
				// The horizontal position
				nx = (ux * t);
						
				// s = ut + 0.5at^2
				ny = (uy * t) + (0.5 * (g) * Math.pow(t, 2));
				
				// Apply the positions	
				$(self).css({'bottom' : (ny)+'px', 'left' : (nx)+'px'});
				
				// Increase the time by 0.10
				t = t + 0.30;
				
				// If the time is greater than the total time clear the interval
				if(t > totalt) {
					
					clicked = false;
					first = true;
					
					
					$('.clipped-box').css({'top' : '-1000px', 'transition' : 'none'});
					$(self).css({'left' : '0', 'bottom' : '0', 'opacity' : '1', 'transition' : 'none', 'transform' : 'none'});
					
								
					// Finally clear the interval
					clearInterval(z);
					
				}
					
			}, 30); // Run this interval every 10ms. Changing this will change the pace of the animation
		
		});
		
	}
	
});

To allow for the animation to be run again, I did another quick interval function that moves the div back to its original position if the animation is finished.

				
	r = setInterval(function() {
				
				
		// This is a bit rough but it does the job
		if(first === true) {
				
	
			// I've just put this in so the deleted box will come down again after its been clicked.
			// That way you can keep pressing delete.
							
			$('.clipped-box').css({'top' : '0', 'transition' : ''});
			$('.clipped-box div').css({'opacity' : '1', 'transition' : '', 'background-color' : ''});
						
			$('.content').css({'display' : 'block'});
				
			first = false;
			
		}
				
	}, 300);
});

Finished!

And that’s it. Now you too can have an exploding HTML block that turns into tinier pieces as disappears off screen. I’ve noticed a few performance issues on default mobile android browsers, which is probably due to problems running the interval. Apart from that, it works in smoothly in the latest version of all browsers. You can change the number of blocks by increasing the value of the amount variable, but it can get a bit processor intensive. I hope you enjoy exploding divs!