Using jQuery for Background Image Animations
After reading Dave Shea's article on CSS Sprites using jQuery to produce animation effects, I felt like playing around with things to see what could be done but accomplish it with a simpler HTML structure (no need for adding superfluous tags) and simpler code, too.
Changing the position of the background image felt to be the best approach to creating the type of effect we're looking for (and I'm not the first to think so: see the examples at the end of this article).
jQuery is a great library for this type of task but out of the box, it can't animate background position properly because of the need to animate two values instead of just one (too bad not all browsers implemented the non-standard background-position-x and -y like Internet Explorer). You'll have to use the Background-Position plugin that is linked in the demo (the original plugin is no longer available on the jQuery site). Previous versions didn't support negative or decimal values properly.
The HTML
Nobody likes adding extra HTML to pull off all the fancy stuff and therefore, we're looking at a very simple unordered list:
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
The Basic CSS
For this, I've just floated the navigation and set each of the links to display block with a little padding. Nothing fancy (which is the whole point).
ul {
list-style:none;
margin:0;
padding:0;
}
li {
float:left;
width:100px;
margin:0;
padding:0;
text-align:center;
}
li a {
display:block;
padding:5px 10px;
height:100%;
color:#FFF;
text-decoration:none;
border-right:1px solid #FFF;
}
li a {
background:url(bg.jpg) repeat 0 0;
}
li a:hover {
background-position:50px 0;
}
Notice that a hover state has been declared. Users who visit the page without JavaScript will, at least, still be able to view the final state. I've declared the background on the li a
separately to make it stand out, but an initial background position needs to be set along with the background image you want to use for the effect.
The Image
You can pull off different types of effects by creating your image in a particular fashion.
Figure 1: The Swipe
In Figure 1, the before and after states are on the left and right but a simple slant can create an interesting effect.
Figure 2: The Fade
Figure 2 is a little more elaborate. The amount of visible space in the normal and hover states are at the very top and bottom of the image. The large gradient in the middle generates a fade-in/out effect when animated over time. The larger the gradient, the less it'll feel like it's moving in from the bottom and feel more like it's actually fading in and out.
The simplicity of the image allows the file size to be small, as well. More complex animations with more colour depth would require a larger file size. Always strike a balance between the effect and performance.
Check out the demo page to see how these two effects (and a couple others) look in action.
The Script
Finally, the script to put this altogether is really straightforward. The animation needs to run when the user moves their mouse over and out of the navigation.
$('#nav a')
.css( {backgroundPosition: "0 0"} )
.mouseover(function(){
$(this).stop().animate(
{backgroundPosition:"(0 -250px)"},
{duration:500})
})
.mouseout(function(){
$(this).stop().animate(
{backgroundPosition:"(0 0)"},
{duration:500})
})
The elements are captured via the $
function and given a default CSS. This is necessary for both Internet Explorer and Firefox 2, which don't report the default background position. In IE, you can get around the issue using background-position-x and -y but it's easier just to set the initial values in the script.
Then the elements are animated using the mouseover
and mouseout
events.
The key thing to note is that any animation is stopped before attempting to animate again. This avoids animations queuing up from repeatedly moving the mouse in and out of the element.
jQuery also has a hover
method which can be used instead of separately using the mouseover and mouseout methods. The reason I didn't was because I like the clarity that is provided by explicitly declaring them. The hover method takes two parameters: the function to run when over and the function to run when out.
The Demo
On the demo page, I've created four separate animations. Just hop to the source to see how it was all put together.
Other jQuery Examples
As mentioned, I'm certainly not the first to come up with this idea. Check out these other resources for more jQuery navigation animation exploration.
- Create a Cool Animated Navigation with CSS and jQuery [nettuts.com]
- CSS Sprites2 - It's JavaScript Time [alistapart.com]
- Animated navigation items using jQuery [tyssendesign.com.au]
- Garage Door Style Menu [css-tricks.com]
Update: Want to know how to handle active state? I got you covered.
Conversation
This is very nice. Who needs Flash?
This is very cool, I love how something so simple can create such an interesting effect.
I can already think of some places I'd like to use such an idea.
Very slick. That top-down is my favorite. This is a poster child example for progressive enhancement (or graceful degradation, depending on how you look at it).
Great method! Definitely will be in my upcoming projects.
The example does not work in IE and Firefox, it seems to me that there was a problem with JavaScript.
It's a very cool effect but I tried to desactivate JavaScript and it wouldn't do anything. I mean, there's no alternative. Is it possible to have that cool JQuery effect, and, IF there's no JS, have the "simple" CSS effect ?
@Pedro works in Firefox here, check your browser
That's one of the cooler jQuery things I've seen lately.
Jonathan, brilliant. Its one of those :: smack head :: "why didn't I think of that" ideas. Well written as well, thanks for sharing.
I also like that you get a 'bonus' effect if you roll in and out of the button very quickly on the animated image examples, since the image simply tends to animate toward the final state, rather than jerking to the beginning of the mouseout state.
I updated the article to highlight the issue with setting the initial CSS, which should fix both IE7 and FF2 issues.
@jthomas: the demo page shows the noscript option which is what it could look like for users with no javascript.
it is very comfortable to use hover pseudo event (http://docs.jquery.com/Events/hover#overout) instead of mouseover and mouseout events in such cases -- less code
Adding the easing plugin would add an extra bit of icing to the cake here (though I think there are two core easing options as well).
Thanks Snook! I was a bit aggravated with Dave's demo and all the extra classes he had in the code. I also did not like the fact that he used a hidden div to overlay over the menu items. Snook 1, Shea 0.
Examples C & D can be accomplished without images by using the official colour animations plugin and the animate() function.
@James: indeed it could. However, interestingly, the use of image + background-position plugin may end up smaller than using the color-animation plugin. But always nice to have options. Thanks for pointing it out.
I too like this better than Dave's version which I had started incorporating into a new design buit will now switch, however this version doesn't have the "active" state setup. I guess that would be easy to do... but I'm lazy and would rather copy/paste.
Great writeup Jonathan! As I was reading Dave's ALA article I kept thinking the same thing...why wouldn't you do it with an animated background? I'll definitely be using this type of effect on future projects.
While it isn't nav/sprites related, I recently used JQuery and the background-position plugin on a client project. Thought I might as well post up a quick demo here in case anyone is trying to do something similar.
Background Image Animation Demo
Really, really nice.
I like it, think I will definitely use this. Although I gotta add that I prefer mootools to jquery.
nice,
I really like the second example with the wave.
I also love how you can endlessly chain methods together in jQuery.
Wow, this is simpler and more elegant than my article for sure. I never even thougt of background-image animation. Nicely done.
I've been using this effect for a while, playing with animation the opacity property for really interesting effects!
Thanks for the stop().animate(...) bit, I was missing the stop() and my animations queued up. To prevent that I tried animate( queue:false, ... ) but that didn't work as expected.
I like it so much.
And I will use in any future project for sure.
Excellent. Loved the ALA article but thought it was a little heavy but this is excellent.
Now to find a site I can use it on!
Wow. I can't believe how much that looks like flash. Great effect.
This rocks! Thanks
Looks nice, but in the end its just the continuation of manipulations of the position of background-images with CSS (like with image-rollovers). Only the creativity sets the limits of animating CSS properties with JS.
Just an idea: Maybe its more helpful to a greater audience, if such things are described in a more general way and more as an impulse to get started. There are still many other JS frameworks out there and in use.
This is great. I have seen a few other ones in recent weeks, and used a hybrid myself, but I like this approach. One accessibility enhancement suggestion, however: use the focus and blur events to run the same code as mouseover and mouseout events, for keyboard accessibility. I just tried it locally and it worked quite nicely with keyboard access too.
Another cool trick is to use
instead of the .stop().animate()
It will actually leave the original animation to finish instead of stopping and restarting it.
this literally could not have come at a better time! I've been fooling around with a menu using the sprite technique and the client really wanted flash. I just didn't see the need for the menu to be flash. This plug-in will work perfectly!
Thanks Snook - You rule!
^__^
Awesome plugin. But how do I create a variable that retrieves only the x background-position of the element and then use that variable in place of the x in backgroundPosition?
Here is my n00b attempt
Beautiful animated, great, not need flash!
Really impressive.
Very Nice Jonathan! I like that there is no additional markup required. Keeps everything clean and simple. Kudos on the clever use of background-position.
thanks for the tutorial!
Jojo Siao.
Thanks.
Realy gj.
Seriously sweet this! I really like using the gradient image for a subtle color fade.
When I turned off Javascript, the hover states do not turn on. This is a specificity issue with the CSS. I see you addressed this sorta, but not completely. Adding this the CSS seems to fix the issue:
#a a:hover,#a li a:focus,#a li a:active,
#b a:hover,#b li a:focus,#b li a:active {
background-position:-150px 0px;
}
#c a:hover,#c li a:focus,#c li a:active,
#d a:hover,#d li a:focus, #d li a:active {
background-position:0px -250px;
}
Dave: that's why I presented a no script option at the top of the page. The other examples demonstrated the script. It was an exercise for the user to put it all together. :) (and thanks for doing so...)
I will use it. thanks
Wow, thanks a lot! This will be a great thing for clients who want to have Flash navigation on their websites. You could also do color fade effects in combination for additional visual trickery.
Nicely done Jonathan!
I wrote a similar tutorial a while ago that demonstrated how can you fade between two images to create a similar effect. I took a similar approach to yours. For those interested, it can be found at http://swedishfika.com/2008/03/04/creating-a-fading-header/
Great job.
Simple and at the same time pretty usage of jQuery. I wrote a russian translation of this article at http://habrahabr.ru/blogs/webdev/40801/ and it achieved a lot of positive feedback. Thanks a lot.
Jonathan: Nice post. FYI - perhaps it's the firewall I'm behind, but I get 403 (Unauthorized) errors (via Firebug and Safari error console) for both the core jQuery library and your plug-in .js files.
thanks, these are some great example and will be very useful :)
Liking this alot, it could produce some awesome effects. Even saw it in use a couple times already, it sure is some good eye candy.
I turned this technique (specifically Shea's CSS Sprites2) into a jQuery plugin:
http://www.newmediacampaigns.com/page/css-sprites2-refactored-building-an-unobtrusive-jquery-plugin
Hopefully it is helpful!
nice article. exactly what i needed. thanks jonathan.
very nice script!
Superb post my friend. We need more posts like this (hint hint) ;)
"Who needs Flash?"
Abso-frickin-lutely! DOM animation for the win! Beautiful stuff, I may be using something similar of my own in the near future. Cheers for the infos!
...and is that CakePHP I see powering this site? Kudos to you if it is :)
Robin
Why didn't i think of this? So simple, yet so effective, i love the examples too. Thanks !
Excellent! Now I've got a bigger challenge for you: How about changing the opacity of the background image, or at least simulating such an effect, without adding to the complexity of the XHTML markup? I've already spent some time working with this but alas without success.
Excellent - I've stolen it as part of my default toolset from hereon in...
Very great and original plugin. jQuery meets flash!
im really impress of this technique. thanks fro sharing.
Probably the most elegant method I've seen. Really very simple code.
Very nice! Clean code, simple jquery, thanks!
Is there a way to leave the background in it hover position is a certain class is attached the li?
Ok this awesome! Thank you!
i liked the effect especially the first one, did you have any prototypeJS plugin who do the same <= [lazy develloper] lol
Something to be aware of is jQuery's hover function actually uses the mouseenter/mouseleave events (either the proprietary IE version or an emulation of them in other browsers) whose behavior is somewhat distinct from mouseover/mouseout.
Well I copied everything verbatim from the test page (images, js, the whole bit) to play with in on our test server, but it doesn't work. At all. Very strange. The "wave" animation gets stuck in the middle, and the rest get stuck too. Oh well, off to find another slick jQuery navigation trick. :(
Inspired by what I saw on this post I'd hoped it could be replicated in Script.Aculo.Us so I could apply it to my existing apps.
Sadly Scriptaculous' *Effect.Morph* doesn't anticipate dual values so can only move backgrounds horizontally (it's been logged as a bug back in July).
Delighted by your examples, I've ported the methods over into an extension for Scriptaculous so now it works. Hopefully they'll see your examples and support it natively soon.
Cheers for sharing all your hard work.
saya ingin buka blog foto
this script absolutely does not work. i would not waste your time. the author has obviously used additional scripting to get it to work. This tutorial was written for search engine ranking and nothing else. Free stuff gets hits. :(
Derrick: you can view source to see exactly how it's been done. I can't make it do something special that nobody can see. If you're having problems, ask questions.
**TUTORIAL ADDED TO JUNK TUT DATABASE**
This tutorial has been added to the Junk Tut Database as part of the Junk Tutorial Project to be launched soon to help unsuspecting web users avoid time-wasting tutorials such as this.
Thanks
Snook, thanks so much for taking the time to write this. Works great.
@derrick, just because you aren't competent enough to get this to work doesn't mean its broken. It works just fine for me. Please take your arrogant and threatening comments somewhere else. I'm sure you will be added to the akismet spam DB if snook uses it, at least I would.
@drew,
I guess you're not too sharp. You have to copy and paste script off others to get the job done. I'm not the one posting junk code.
@drew,
you also need to read the akismet site again. my comment hardly qualifies for spam. crying wolf to akismet is a good way to get your own site banned.
Worked out the issue with Derrick via email. There was a missed file in the setup.
The problem was actually partially solved.
@derrick: no, it was completely solved. I just double-checked the test page you had sent me via email and the one animation that doesn't "work" is because you're animating along the X axis when the image only changes along the Y axis.
We've put this concept into use in our game, http://www.vertigo-project.com/projects/redline-game
Essentially the background 3 divs (road, grass and cloud) move at varying speeds by just adjusting the background position...
Snook lovely work. How hard would it be to apply this to a whole page background I wonder !?
@Tim: seems like it would apply in the same way. just change "#nav a" to "body" or whatever and give it a try.
Very nice. I've never tried jQuery before, but this inspires me to give it a look. Thank you.
I wonder if this works with different lengths of menu.
Anyone, any idea??
It should work with whatever length of menu you have, assuming the image you use is designed for the length of the menu item.
Hello. Great method, i love it. I just want to ask if it would be posibile for a FADE transition. fade background images one to another. Thank you!
I'm getting a flickering effect on IE6, anyone have a fix for this?
When will people stop using that browser so I can ignore it?!
Hello Jonathan,
You are probably very busy but I want to add the effect : When you click on a header link, the background stay in the second color (active tab). In wordpress there is a "current_page_item" we must add in CSS to do so but I don't success...
If you could explain how to do so with jquery or css...
Thanks a lot !
Great post! I just found this awesome jQuery Cheat Sheet for the iPhone. I've only had it for a day and it's been really helpful.
http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=302090867&mt=8
Looks like there's also a CSS Cheat Sheet from the same guys.
http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=301093674&mt=8
Great post! This opens up doing some new types of mouseovers I hadn't thought about before.
Thanks for posting!
Very nice. actually the background animation can have some variations. Simply adding a png format image serving as a text mask can create a very nice effect. I have made a page according to your inspiration. see my site for demonstration
This is great, love the simplicity of it and it works like a charm, well done and thanks for sharing.
I'm trying to add a class selected to it.
anyone have an idea on how to go about this?
@derrick it works fine! Same as per this demo page, try to install the script again.
Thank you Jonathan. I love this way for buttons, much better than old animated .gif images :) and quicker too. Will play around possibly adding some CSS in addition. cheers
its really cool,,, thnx man
Can't get this to work with jquery-1.4.4.min.js but is fine with 1.2.6.min
Wonderful Jonathan - thank you.
@ world, who is this Derrick guy?
Darn. I've been trying to get it to work with jQuery v1.4.4 as well. Anyone have any luck? What should one change to make it work?
(thanks for any help!)
This is great, but what is the dimension of the background image for the remainder versions? I like the one "top down" one. Thanks!
hello. i love this idea and currently trying to implement on my site but having some issues with handling active state. is there a way to disable mouseout animation once clicked? Thanks in advance!
@June: Check out the follow-up article on active state. This might explain what you need.
Jonathan, thank you for the quick reply. Follow-up article makes sense but I was hoping you would explain how to do it using 'onclick', 'addclass' type of route or add conditions at the beginning. The reason for this (for me) would be to 'include' menu as part of header through php. Well maybe when you have time you could expand this tut more.
@June, in that case, you'd add an click handler that toggles an active class. Then include a conditional that checks for the existence of the active class before continuing the animation.
@Jonathan,
This is a wonderful snippet of code. It took me a little bit of a moment to figure it out (due to my lack of solid CSS and ASP.NET masterpage knowledge), but it was fairly easy to follow. Any ideas on how to get rounded edges for this concept, without any IE/other browser hacks?
Jonathan,
thank you for funtastic plugin! I can see that your example works just fine in IE7 but mine shows only hover state. Do you have any idea why? Thanks again.