Heygrady (there's a new blog)

I don't get It.

Animating With Curves in jQuery


jQuery has a really strong animation library built in that makes it very easy to animate a CSS property with very little JavaScript. However, it's not well suited to animating complex things that are not directly related to CSS. In particular, tweening an element along a curve or animating a canvas — which doesn't use CSS at all — is more difficult.

To solve these issues I've created two new jQuery plug-ins: jQuery Tween and jQuery Curve.

Tweening an object.

Tween simply uses the existing jQuery animation functions to manage the animation frames but leaves the application of the animation to the user. This essentially means that instead of animating a specific CSS property, tween simply fires a callback function. This is very similar to using the step callback option for jQuery animate but it removes some of the calculation overhead. Tween makes it easy to animate something like a canvas element which jQuery animate doesn't handle by default.

In the example above, a sine wave is drawn on a canvas over 3 seconds. The first argument in the tween plug in is a step function while all other arguments match the default jQuery animation function. The step function receives 2 arguments, the current value and the fx object. When using the tween function, the current value passed is always equal to the current animation time. When using jQuery animate the current value is the value of the CSS property being animated. As the example above shows, the tween plug-in makes it much easier to animate something besides a standard CSS property.

Conceptually, the tween plug-in is similar to animating a custom property with jQuery's built-in animate function. The example below shows the exact same behavior as the example above using the native jQuery animate with a custom property instead of using the tween function.

While using animate directly is similar to tween, animate causes jQuery to perform a series of property lookups and calculations on initialization that are skipped with tween. Additionally, tween adds a special step function to the fx object that overrides jQuery's default behavior to avoid any additional calculations on each step of the animation.

It's also worth noting that the normal jQuery animate calls the step function once for each property being animated. So if you're animating five properties (height, width, etc.) with the native jQuery animate function, a step function is called five times for each step of the animation. The tween plug-in avoids these extra function calls.

Using Curves

The curve plug-in contains a handful of useful functions for plotting curves as a function of time and works well with the tween plug-in. The curves supported natively are circle, ellipse, sine, and bezier curves. It would be possible to define additional curves quite easily by extending the curve object. Each function takes a similar set of options and returns x and y coordinates.

The example above uses a curve function to draw a sine wave. The curve function actually returns 3 values in the array: x, y and the angle of the tangent line. The tangent angle can be used to rotate an object to follow the curve. As you can see, the curve function makes it easier to calculate a sine wave as a function of time.

A curve as a function of time

jQuery animate is using a simple concept of percentage of completion to perform animations. In a simple 1 second animation — let's animate the height property — jQuery will look at the current height value and the final height and calculate the difference. Next jQuery will calculate the current time and the duration — again, 1 second in this example — and start an interval timer. At each interval the percentage of the duration is calculated. That percentage is essentially the current time divided by the final time. The percentage is then altered by the easing function to speed up or slow down time before it is applied to the value in question.

Consider animating the height from 0px to 10px over 1 second. At 500ms the animation is 50% completed. In a step function the fx object reflects this as an fx.state of 0.5. The fx.pos will be slightly different based on the easing function; but with the default easing it is 0.4999. The current height is the start value plus the difference multiplied by the current position. This means 0px + 10px * 0.4999 which is 4.999px. Of course the differences between fx.state and fx.pos can be more dramatic depending on the fx.state. For instance, an fx.state of 0.25 corresponds to an fx.pos of 0.1464 using the default easing.

The value of fx.pos is essentially a percentage of completion and is important for plotting curves. Any curve, like a circle, has a start and an end point. Simple trigonometry can be used to calculate any point on a circle at a certain time. This makes it trivial to use such a function for drawing a curves as the example below shows.

The example above isn't animating the circle, it's drawing it as a series of line segments using a loop. The current position on the circle is calculated for each iteration of the loop. Even though it isn't animated, the circle is still drawn as a function of time. NOTE: if all you want to do is draw a circle on a canvas there's easier ways.

Using the tangent angle

In addition to the x and y coordinates, the curve functions can also return a tangent angle which can be used to rotate the element in order for it to follow the curve. The tangent is returned in radians. In the example below, the angle of the rocket ship angle is set to the tangent angle so it will appear to follow the sine wave as it moves.

Putting it all together

The example below shows each of the supported curves in action. The tween plug-in is used to animate and rotate a rocket ship along a curve while simultaneously drawing the exact same curve on a canvas. This demonstrates the utility of separating the jQuery animate functionality from straight CSS operations — animating a div and drawing on a canvas at the same time.