Skip to main content
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Jin Park
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Jin Park

Using jQuery's animate() Method To Power Easing-Based Iteration

By on

Last night, I was reading about jQuery animation when I had the thought that you could use the animate() method to power iteration in which the iteration-step was implemented with an easing function rather than with a liner incrementation. A while back, I demonstrated that the step callback of the animate() method could be used to create complex, custom animation; but, I think you could abstract that concept out even further to create animate-powered iteration. Now, why would you want to do this? I have no idea; but, it seemed like a fun thing to try.

In the following code, I am adding the ease() function to the jQuery namespace:

jQuery.ease( start, end, duration, easing, callback )

To relate this concept back to for-loop iteration, you can sort of think of the above as being equivalent to the following pseudo code:

for (var i = start ; i <= end ; i = easing()){
	callback( i );
}

When the callback gets invoked for each step of the iteration, I am passing in the following parameters:

  • Index: The current value of the iteration index.
  • StepIndex: The current iteration (increments 1 for each callback invocation).
  • EstimatedSteps: The rough estimation of how many times the callback will be invoked.
  • Start: The original start value.
  • End: The original end value.

To see this in action, take a look at the following code:

<!DOCTYPE html>
<html>
<head>
	<title>Using jQuery's Animate / Easing For Iteration</title>
	<script type="text/javascript" src="./jquery-1.4.2.js"></script>
	<script type="text/javascript">

		// I am the easing iteration funciton. This is built on top
		// of the core animate function so that it can leverage the
		// built-in timer optimization.
		jQuery.ease = function( start, end, duration, easing, callback ){
			// Create a jQuery collection containing the one element
			// that we will be animating internally.
			var easer = $( "<div>" );

			// Keep track of the iterations.
			var stepIndex = 0;

			// Get the estimated number of steps - this is based on
			// the fact that jQuery appears to use a 13ms timer step.
			//
			// NOTE: Since this is based on a timer, the number of
			// steps is estimated and will vary depending on the
			// processing power of the browser.
			var estimatedSteps = Math.ceil( duration / 13 );

			// Set the start index of the easer.
			easer.css( "easingIndex", start );

			// Animate the easing index to the final value. For each
			// step of the animation, we are going to pass the
			// current step value off to the callback.
			easer.animate(
				{
					easingIndex: end
				},
				{
					easing: easing,
					duration: duration,
					step: function( index ){
						// Invoke the callback for each step.
						callback(
							index,
							stepIndex++,
							estimatedSteps,
							start,
							end
						);
					}
				}
			);
		};


		// -------------------------------------------------- //
		// -------------------------------------------------- //


		// Ease from star (1) to finish (100).
		$.ease(
			1,
			100,
			1000,
			"swing",
			function(){
				console.log( arguments );
			}
		);

	</script>
</head>
<body>
	<!-- Intentionally left blank. -->
</body>
</html>

As you can see, I am defining an iteration from 1 to 100 over the course of 1000ms using the "swing" easing logic that comes with the jQuery core library. When I execute this iteration, the following arguments are logged to the console:

[1.0002442725080185, 0, 77, 1, 100]
[1.0021984381069835, 1, 77, 1, 100]
[1.0549511893824843, 2, 77, 1, 100]
[1.3162401292595844, 3, 77, 1, 100]
[1.5386187677878915, 4, 77, 1, 100]
[1.9360193865549626, 5, 77, 1, 100]
[2.59404353372116, 6, 77, 1, 100]
[3.188245767482659, 7, 77, 1, 100]
[4.086498284786836, 8, 77, 1, 100]
[5.3855378065454635, 9, 77, 1, 100]
[6.047634999849525, 10, 77, 1, 100]
[6.899998311311322, 11, 77, 1, 100]
[7.735790848954654, 12, 77, 1, 100]
[8.789299598058602, 13, 77, 1, 100]
[9.823552772835283, 14, 77, 1, 100]
[11.103130839289712, 15, 77, 1, 100]
[12.359594482598432, 16, 77, 1, 100]
[13.78378224943241, 17, 77, 1, 100]
[15.169889687317145, 18, 77, 1, 100]
[17.30106396143236, 19, 77, 1, 100]
[19.067489633276345, 20, 77, 1, 100]
[20.903703338652814, 21, 77, 1, 100]
[22.805628223942726, 22, 77, 1, 100]
[24.769041540274877, 23, 77, 1, 100]
[26.789584019084327, 24, 77, 1, 100]
[28.583467563574366, 25, 77, 1, 100]
[30.556131432173697, 26, 77, 1, 100]
[32.4225127863664, 27, 77, 1, 100]
[34.17215696448678, 28, 77, 1, 100]
[35.79644171193677, 29, 77, 1, 100]
[37.43828402671404, 30, 77, 1, 100]
[39.247104825491, 31, 77, 1, 100]
[40.614355964536855, 32, 77, 1, 100]
[42.29606192000807, 33, 77, 1, 100]
[44.14175320508501, 34, 77, 1, 100]
[46.30629455396736, 35, 77, 1, 100]
[48.168230689872686, 36, 77, 1, 100]
[50.34449141944896, 37, 77, 1, 100]
[52.36566404216176, 38, 77, 1, 100]
[54.84863422926178, 39, 77, 1, 100]
[56.39530942469606, 40, 77, 1, 100]
[58.397061818725085, 41, 77, 1, 100]
[60.385644035463145, 42, 77, 1, 100]
[62.50866192437266, 43, 77, 1, 100]
[64.1606576911129, 44, 77, 1, 100]
[66.23934837470433, 45, 77, 1, 100]
[68.14238799630591, 46, 77, 1, 100]
[70.01600485497607, 47, 77, 1, 100]
[71.71667929686885, 48, 77, 1, 100]
[73.66252580589837, 49, 77, 1, 100]
[75.42934848097015, 50, 77, 1, 100]
[77.15459570091967, 51, 77, 1, 100]
[78.83539021693224, 52, 77, 1, 100]
[80.46892891535957, 53, 77, 1, 100]
[82.05248749256015, 54, 77, 1, 100]
[83.58342499830574, 55, 77, 1, 100]
[85.0591882401769, 56, 77, 1, 100]
[86.58394705735986, 57, 77, 1, 100]
[87.83544334643715, 58, 77, 1, 100]
[89.2283451235411, 59, 77, 1, 100]
[90.36274034270053, 60, 77, 1, 100]
[91.61449701019272, 61, 77, 1, 100]
[92.62422684883724, 62, 77, 1, 100]
[93.57409585614153, 63, 77, 1, 100]
[94.60482294732421, 64, 77, 1, 100]
[95.485584276624, 65, 77, 1, 100]
[96.23203685930869, 66, 77, 1, 100]
[96.96732595386678, 67, 77, 1, 100]
[97.67247740510233, 68, 77, 1, 100]
[98.245592213661, 69, 77, 1, 100]
[98.73908020293557, 70, 77, 1, 100]
[99.15211836755276, 71, 77, 1, 100]
[99.48401787083121, 72, 77, 1, 100]
[99.7342251935759, 73, 77, 1, 100]
[99.90232305719944, 74, 77, 1, 100]
[99.98803111963072, 75, 77, 1, 100]
[100, 76, 77, 1, 100]

Internally, the ease() function creates a detached DOM node and then uses jQuery's animate() method to "animate" a fake CSS property that will act as our iteration index. Then, we simply hook into the step-callback in order to invoke the given iteration callback, passing in our current iteration value.

It would have been simple enough to bypass the animate() method and set up my own timer using setInterval(). However, Javascript timers are expensive to use. jQuery handles this cost quite gracefully by creating a central timer from which all animate() methods are powered. As such, I figured it would make the most sense just to leverage the optimized infrastructure that jQuery already had in place.

Again, I am not sure why anyone would want to use this; but, it popped into my head last night and I wanted to see if I could make it happen. Of course, this implementation presupposed that one would want to use a duration over which the iteration would take place. We could remove the duration aspect altogether, which gives me a new idea... but more on that later.

Want to use code from this post? Check out the license.

Reader Comments

15,663 Comments

@Sathya,

Oh cool - glad you found it useful. I think the hardest thing about this, as far as generic usefulness, is that it requires the concept of a "duration" in addition to a start/end. Although, I suppose you could just think of this as the "step" portion of a traditional for-loop.

That said, I think it is cool and useful for specific things.

2 Comments

Hi.

I stumbled upon your post on my search for a way to ease the changes on a temperature dial I'm trying to create for my Arduino weather station. I tried to figure it out on my own, but because I was using setTimeout and setInterval I couldn't incorporate a duration into the ewuation. So, first of all - THANK YOU :)

I have a small problem though - the easing always starts from 0, no matter what number I set as the start parameter.

I've set up a jsfiddle for testing, so maybe you can check it out and tell me what's wrong?

Here's the link: http://jsfiddle.net/witq/qtTKX/

2 Comments

@Witek,

Aaaaand it's done. I changed the animated property from easingIndex to some existing css property - width. And it started working as expected.

1 Comments

@Witek,

Thanks! That was just what I needed.

@Ben,

I used this to animate a range input at page load. Thanks!

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel