Animating Rotation With jQuery

Animating Rotation With jQuery


Shortly after it was announced, I bought the Hype App in the Mac App Store (at a promotional price) which allows you to "create beautiful animated and interactive web content without any coding required", in a WYSIWYG editor. (It generates HTML5, CSS3 and JavaScript output – no flash.) I didn’t have anything specific in mind when I bought it, I just bought it out of mere curiosity. And I never really had a good look at it until just recently.

I started creating a new version of using Hype, but I was quickly disappointed to see that all the content was contained in a JavaScript file – not exactly SEO friendly. I didn’t exactly need any interactivity or animation either, I just wanted to experiment with it and hopefully learn something along the way. So I ended up scrapping that idea and decided to hand-code it myself using jQuery and CSS3. Nothing too complicated though.

I also wanted to make sure that the site looked/worked well even without JavaScript, (see "Progressive Enhancement" on Wikipedia), which is worth mentioning, but never a huge obstacle.

Once I completed the HTML5 markup and styled the page with CSS, I moved on to the JavaScript (using jQuery) and had to read up on the .animate() method. All I wanted to do was start with an image centered on the page (hiding all other content) and then move the image to its final position on the page, while shrinking it and rotating it – but all in a single, smooth animation. (And then unhiding the rest of the content of course.) Pretty simple and straightforward.

The only problem was the rotation part. There were a couple of solutions I found online, most of which required the use of a rotation plugin, but nothing that I actually liked. I knew there had to be a simpler way, and there was.

Here’s what I came up with:

$(document).ready(function() {
	// load our starting animation
	var photo_position = $('#photo img').position();
	$('.start #photo img').center().animate({
		left: photo_position.left,
		width: "242px",
		height: "182px",
		marginTop: "20px",
		marginRight: 0,
		marginBottom: "40px",
		marginLeft: "-10px"
		duration: 3000,
		step: function(now, fx) {
			var degree = 360 - (5 * fx.state);
			$('#photo img').css({
				'-webkit-transform': 'rotate(' + degree + 'deg)',
				   '-moz-transform': 'rotate(' + degree + 'deg)',
				    '-ms-transform': 'rotate(' + degree + 'deg)',
				     '-o-transform': 'rotate(' + degree + 'deg)',
				        'transform': 'rotate(' + degree + 'deg)'
		complete: function() {
			$('#photo img').css({
				position: "relative",
				left: 0,
				right: 0
			$('#main-header, #main, #contact, .links, hr, #google-ads, #main-footer').css('opacity',0).animate({
				opacity: 1

You can see that I’m calling a .center() method (which doesn’t natively exist in jQuery) before the .animate() method, and Here’s the code for that:

// add a function to jQuery to center our element = function () {
	this.css("top", (($(window).height() - this.outerHeight()) / 2) + $(window).scrollTop() + "px");
	this.css("left", (($(window).width() - this.outerWidth()) / 2) + $(window).scrollLeft() + "px");
	return this;

There’s nothing at all complicated about the animation and the JavaScript snippet above should be very easy to follow. The first part is just a CSS map of the final destination for the photo, and the last part is just a bunch of CSS to apply once the animation is complete. The actual rotation (with jQuery) is in the middle section, using the step option. Here’s what the documentation says about this:

The second version of .animate() provides a step option — a callback function that is fired at each step of the animation. This function is useful for enabling custom animation types or altering the animation as it is occurring. It accepts two arguments (now and fx), and this is set to the DOM element being animated.

  • now: the numeric value of the property being animated at each step
  • fx: a reference to the jQuery.fx prototype object, which contains a number of properties such as elem for the animated element, start and end for the first and last value of the animated property, respectively, and prop for the property being animated.

I used the Developer Tools in Safari to get a list of all the properties for fx. Among many others, I found the state property, which basically returns a decimal value representing the current state of the animation from 0.0 (beginning) to 1.0 (end). I’m using the value of state to calculate how many degrees the element should be rotated and then apply this value using CSS transforms. Since the rotation is incrementally applied in many steps (and in fractional/decimal values), it results in a very smooth animation. Many of the other solutions I found only applied the rotation in single-digit degrees at a time.

Here’s an important note about the var degree = 360 - (5 * fx.state); line though: I had to think about this for a little while in order to get it right. I knew that I wanted my image to be rotated -5º (or 355º) so originally, I had just gone var degree = 355 * fx.state; and while that did result in my image ending up at the correct angle at the end of the animation, it also made my image rotate in almost a complete circle – from 0º…90º…180º…270º and eventually to 355º – not the effect I was going for. What I wanted is for the image to rotate 5º counter-clockwise, not clockwise. So essentially 360º…359º…358º… all the way to 355º. You can see the solution I came up with. At the beginning of the animation the image is rotated 360 - (5 * 0) = 360º degrees; at the halfway point, 360 - (5 * 0.5) = 357.5º and ultimately 360 - (5 * 1) = 355º degrees at the very end. (But again, the degrees get calculated through many fine steps from 0 to 0.00001 to 0.1 to 0.8888 etc., so the animation is very smooth.) If you wanted a clockwise rotation, then you would just do it how I originally had it: var degree = 355 * fx.state;

Not so complicated after all, no?

I respect your privacy and don’t inundate you with obtrusive ads. If you found this useful, please consider making a donation.