jQuery: the fx object

The jQuery's fx object forms the core of all jQuery effects. In its essence, this object handles CSS styles and timers used during jQuery's effects. In fact, all jQuery's effects are basically a combination of CSS properties and timers. When you have to create an effect, you have to keep track both of the current style property and the interval when your style change occurs. jQuery handles this task with steps. A step is a fragment of an effect. Each effect can be actually broken down into several steps. For example, when you call the hide() method, the CSS display property is set to none. If you don't use steps, this effect will be immediate. Instead, with steps you can add a certain delay to this transition, for example by specifying slow as the first parameter of this method.

The fx object is declared as follows:

fx: function( elem, options, prop ) {
		this.options = options;
		this.elem = elem;
		this.prop = prop;

		if ( !options.orig ) {
			options.orig = {};
		}
}

This object has three properties:

  1. elem: the element where the effect takes place
  2. options: an object containing all the settings for fx
  3. prop: the CSS property currently in use.

Then the prototype object of fx is augmented with several private methods. The first is update():

// Simple function for setting a style value
update: function() {
		if ( this.options.step ) {
			this.options.step.call( this.elem, this.now, this );
		}

		(jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
}

In this case, the step() method is called to change a style property on a specific moment during the progress of the effect. The comes the cur()
method:

cur: function() {
		if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
			return this.elem[ this.prop ];
		}

		var r = parseFloat( jQuery.css( this.elem, this.prop ) );
		return r || 0;
}

This method returns the current numeric value of a CSS property that specifies a length. For example, CSS properties such as top, margin and
width all have a numeric value. The next method, custom(), actually handles animations:

custom: function( from, to, unit ) {
		var self = this,
			fx = jQuery.fx;

		this.startTime = jQuery.now();
		this.start = from;
		this.end = to;
		this.unit = unit || this.unit || "px";
		this.now = this.start;
		this.pos = this.state = 0;

		function t( gotoEnd ) {
			return self.step(gotoEnd);
		}

		t.elem = this.elem;

		if ( t() && jQuery.timers.push(t) && !timerId ) {
			timerId = setInterval(fx.tick, fx.interval);
		}
}

This method starts an animation from one number to another. If no unit is provided, all values are treated as pixels. An animation is divided into three phases:

  1. start: the origin of the animation
  2. destination: the target state of an element after an animation is complete (e.g, its position on the viewport)
  3. end: the end of the animation.

The most important method is surely step(). It is as follows:

step: function( gotoEnd ) {
		var t = jQuery.now(), done = true;

		if ( gotoEnd || t >= this.options.duration + this.startTime ) {
			this.now = this.end;
			this.pos = this.state = 1;
			this.update();

			this.options.curAnim[ this.prop ] = true;

			for ( var i in this.options.curAnim ) {
				if ( this.options.curAnim[i] !== true ) {
					done = false;
				}
			}

			if ( done ) {
				// Reset the overflow
				if ( this.options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
					var elem = this.elem,
						options = this.options;

					jQuery.each( [ "", "X", "Y" ], function (index, value) {
						elem.style[ "overflow" + value ] = options.overflow[index];
					} );
				}

				// Hide the element if the "hide" operation was done
				if ( this.options.hide ) {
					jQuery(this.elem).hide();
				}

				// Reset the properties, if the item has been hidden or shown
				if ( this.options.hide || this.options.show ) {
					for ( var p in this.options.curAnim ) {
						jQuery.style( this.elem, p, this.options.orig[p] );
					}
				}

				// Execute the complete function
				this.options.complete.call( this.elem );
			}

			return false;

		} else {
			var n = t - this.startTime;
			this.state = n / this.options.duration;

			// Perform the easing function, defaults to swing
			var specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];
			var defaultEasing = this.options.easing || (jQuery.easing.swing ? "swing" : "linear");
			this.pos = jQuery.easing[specialEasing || defaultEasing](this.state, n, 0, 1, this.options.duration);
			this.now = this.start + ((this.end - this.start) * this.pos);

			// Perform the next step of the animation
			this.update();
		}

		return true;
}

First, this method uses the done private property to check whether an animation has reached its final state. If so, all CSS properties are changed and then the callback function of each animation is executed (of course if such a callback function has been provided). Otherwise, the animation is started and its steps are all performed by the update() method. Note that in this case both the duration and easing options are used.

This entry was posted in by Gabriele Romanato. Bookmark the permalink.

Comments are closed.