When I was reading the jQuery 1.4 Reference Guide over the weekend, it mentioned that jQuery's animate() method has a step callback function that gets called after each step of the animation has completed. Unfortunately, it didn't say anything more than this. I tried looking at the online documentation and it said the exact same thing. As such, I thought the step callback function would be a good feature to experiment with; I wanted to see what it did and how I might use it.
After running a number of animations and logging the step callback arguments to the console, it appears that the step callback receives two arguments: the current value of the property being animated and an object containing information about the animation. Apparently, the step callback gets invoked for step of each property being animated; while this might not be what you expected, it makes sense since each animating property value changes independently.
To get a sense of what I'm talking about, take a look at this small demo:
As you can see, I am taking a DIV with a left of 500px and I'm animating it down to a left of 0px. The step callback of the animate() method simply logs the current left to the console. When we run the above code, we get the following output:
As you can see, the step callback simply reports the current value of the left property as it is being animated. The number of times that the step callback gets invoked is determined by the animation duration and the selected easing.
I said above that the step callback function receives two arguments, the second of which contains information about the animation. After inspecting this second argument, I simply couldn't find anything useful about it other than that it tells you what property is being animated; none of the other values contained within it seem to be all that relevant.
One of the things that I thought would be interesting would be to use the step callback function to override the current animation. Unfortunately, this cannot be done directly; not only is the step function called after each animation step (not before), should you override the property value, the animation itself will simply override your setting in the next step. But, we can do something a bit tricky! We can use the animate() function to animate irrelevant properties such that we can use the step callback function to power our own animation logic.
jQuery's animate() function is controlled by the initial property value, the duration, and the selected easing method. As such, the step callback reflects the relative position of the current state of the given property within the greater animation. While this is typically based on somewhat arbitrary values, if we pick specific start and end values for a given property, we can being to leverage the step callback in a very custom way.
Image that we had a property that we wanted to animate in a way that the animate() function didn't really provide for. If we chose to animate a completely irrelevant property from 100 to 0 (ex. text-indent), we could think of the value being passed to our step callback as the "percentage" completed in the given animation. The percentage would be determined internally by the animation duration and easing function; but, we could then use that percentage inside of the step callback to power our own animation of the contextual element.
In the following demo, that's exactly what I'm going to do. I'm going to create an image that I can drag with my mouse; then, upon releasing the mouse, the image will drift off in the given direction. As the image drifts, if it hits the sides of the window, it will ricochet off the side, moving back in the opposite direction. This change of direction mid-animation is not something that jQuery's animate() function can do inherently. But, by animating the irrelevant property, text-indent, from 100 to zero, we can use the step callback function to calculate the position of the image manually while still reaping the benefits of the native easing and duration management.
While I know this is a lot of code to look at, if you review the animate() method call, you'll see that it is animating the text-indent property from 100 to zero. While text-indent has no visual bearing on our UI, its animation will give the step callback function an understanding of the percentage animation completed. Within the step callback, I am then using that percentage, implicitly adjusted by easing, to decrease the speed of the drifting image over time. To see this in action, you really need to take a look at the video, or try it for yourself here.
jQuery's animate method is very powerful, especially for more "linear" animations. Not only does it do all of the calculations for us, it allows us to use the stop() method to cancel current animations. While this is great in most cases, we can go a bit further and leverage the step callback function to really start building complex animations that are not inherently supported by the jQuery library.
Want to use code from this post? Check out the license.