(function (jQuery) {
// rotate3Di v0.9 - 2009.03.11 Zachary Johnson www.zachstronaut.com
// "3D" isometric rotation and animation using CSS transformations
// Visual effect currently supported in Safari/WebKit and Firefox 3.1+
var calcRotate3Di = {
direction: function (now) {return (now < 0 ? -1 : 1);},
degrees: function (now) {return (Math.floor(Math.abs(now))) % 360;},
scale: function (degrees) {return (1 - (degrees % 180) / 90)
* (degrees >= 180 ? -1 : 1);}
}
// Custom animator
jQuery.fx.step.rotate3Di = function (fx) {
direction = calcRotate3Di.direction(fx.now);
degrees = calcRotate3Di.degrees(fx.now);
scale = calcRotate3Di.scale(degrees);
if (fx.options && typeof fx.options['sideChange'] != 'undefined') {
if (fx.options['sideChange']) {
var prevScale = jQuery(fx.elem).data('rotate3Di.prevScale');
// negative scale means back side
// positive scale means front side
// if one is pos and one is neg then we have changed sides
// (but one could actually be zero).
if (scale * prevScale <= 0) {
// if one was zero, deduce from the other which way we are
// flipping: to the front (pos) or to the back (neg)?
fx.options['sideChange'].call(
fx.elem,
(scale > 0 || prevScale < 0)
);
// this was commented out to prevent calling it more than
// once, but then that broke legitimate need to call it
// more than once for rotations of 270+ degrees!
//fx.options['sideChange'] = null;
// this is my answer to commenting the above thing out...
// if we just flipped sides, flip-flop the old previous
// scale so that we can fire the sideChange event correctly
// if we flip sides again.
jQuery(fx.elem).data(
'rotate3Di.prevScale',
jQuery(fx.elem).data('rotate3Di.prevScale') * -1
);
}
}
// Making scale positive before setting it prevents flip-side
// content from showing up mirrored/reversed.
scale = Math.abs(scale);
}
// Since knowing the current degrees is important for detecting side
// change, and since Firefox 3.0.x seems to not be able to reliably get
// a value for css('transform') the first time after the page is loaded
// with my flipbox demo... I am storing degrees someplace where I know
// I can get them.
jQuery(fx.elem).data('rotate3Di.degrees', direction * degrees);
jQuery(fx.elem).css(
'transform',
'skew(0deg, ' + direction * degrees + 'deg)'
+ ' scale(' + scale + ', 1)'
);
}
// fx.cur() must be monkey patched because otherwise it would always
// return 0 for current rotate3Di value
var proxied = jQuery.fx.prototype.cur;
jQuery.fx.prototype.cur = function () {
if (this.prop == 'rotate3Di') {
var style = jQuery(this.elem).css('transform');
if (style) {
var m = style.match(/, (-?[0-9]+)deg\)/);
if (m && m[1]) {
return parseInt(m[1]);
} else {
return 0;
}
}
}
return proxied.apply(this, arguments);
}
jQuery.fn.rotate3Di = function (degrees, duration, options) {
if (typeof duration == 'undefined') {
duration = 0;
}
if (typeof options == 'object') {
jQuery.extend(options, {duration: duration});
} else {
options = {duration: duration};
}
if (degrees == 'toggle') {
// Yes, jQuery has the toggle() event but that's only good for
// clicks, and likewise hover() is only good for mouse in/out.
// What if you want to toggle based on a timer or something else?
if (jQuery(this).data('rotate3Di.flipped')) {
degrees = 'unflip';
} else {
degrees = 'flip';
}
}
if (degrees == 'flip') {
jQuery(this).data('rotate3Di.flipped', true);
var direction = -1;
if (
typeof options == 'object'
&& options['direction']
&& options['direction'] == 'clockwise'
) {
direction = 1;
}
degrees = direction * 180;
} else if (degrees == 'unflip') {
jQuery(this).data('rotate3Di.flipped', false);
degrees = 0;
}
var d = jQuery(this).data('rotate3Di.degrees') || 0;
jQuery(this).data(
'rotate3Di.prevScale',
calcRotate3Di.scale(calcRotate3Di.degrees(d))
);
jQuery(this).animate({rotate3Di: degrees}, options);
}
})(jQuery);
