Playing with rotation animation in YUI3

A long time ago in a galaxy far, far away–well, actually, it was just during the Yahoo! Open Hack SEA held in Jakarta, Indonesia–one of the things I got caught up on was working with animations in YUI3–specifically adding a rotation animation. I was high on tea latte and from lack of sleep.

I’ve always meant to come back to it, make it work across browsers, package it up prettily sometime, but I’ve never been able to. This entry has been in the database ever since the hack day, actually. But never put up because I just couldn’t finish it.

But it feels like a waste to let the code (as hackish, and likely useless as it is) fade away, so I’m pushing on and publishing this post anyway.

Animating the element

I wanted to follow as closely as possible whatever convention was used in YUI3, and as such I wanted the code to animate go something like:

YUI().use( 'anim-rotate', function(Y){
    var rotateAnim = new Y.Anim({
        node     : '#rotate',
        from     : { 'rotate' : '0' },
        to     : { 'rotate' : '180' } // we turn #rotate upside down
    });
    rotateAnim.run();
});

The question was, how? It was actually pretty straightforward after I’d taken a look at some code. I needed to add a “rotate” behavior to Y.Anim. Y.Anim.behaviors contain functions that determine what styling changes happen on each “stage” of the animation via the set() function.

Firefox and Safari have -moz-transform and -webkit-transform and they work well, accepting numeric inputs which is pretty much what I needed in order to have Y.Anim calculate the steps needed.

However, Internet Explorer’s BasicImage just doesn’t play nice; it rotates only in 90-degree steps. *throws random stuff at IE* This article discusses an IE implementation that works in increments, but when I tried porting over to the script, it ended up doing rather odd (funny!) things. You can observe the funny sample here, and see for yourself.

I spent two nights trying to figure out exactly why IE was doing this weird flipping out thing: the math was correct if I used a calculator. Rotating the image manually in increments using DXImageTransform.Microsoft.Matrix was working without any of the weird flipping.

Finally, I tried looking at the exact values that were coming out of Math.cos() and Math.sin(). It was usually correct–until the animation gets to 90degrees, 180 degrees, etc.

The culprit

Math.cos() and Math.sin() accepts values in radians, not degrees. So I needed to convert them (degree * Math.PI/180) and then perform the appropriate function. And Math.cos(90*Math.PI/180) is not 0, but rather, 6.123233995736766e-17.

And no, there is no way to round it up to a more moderate value. Math.round() rounds to the nearest integer, not some-decimal-place-I-want.

Yep, boys and girls, they are splitting hairs somewhere.

So apparently, it wasn’t an IE issue. This value came up in all the browsers I tested. IE’s “mistake” was in not giving us rotate() like the other browsers, but I suppose they have better things to do in their time than make things rotate (which is likely for the best…).

In any case, here’s the code I used. Maybe some IE developer will see this and make things work somehow ;)

A quick disclaimer: it’s a hack, and not even cross-browser.

YUI.add( 'anim-rotate', function (Y) {
    Y.Anim.behaviors.rotate = {

        // this function changes the style of the node/element
        set: function(anim, att, from, to, elapsed, duration, fn) {

            // get the actual degrees to rotate the element
            var v = fn(elapsed, Number(from),  Number(to) - Number(from), duration);

            // now set the styling
            anim._node.setStyle( 'transform', 'rotate(' + v + 'deg)' );
            anim._node.setStyle( 'webkitTransform', 'rotate(' + v + 'deg)' );
            anim._node.setStyle( 'MozTransform', 'rotate(' + v + 'deg)' ); // FF 3.1+ only :(

            // IE stuff; doesn't work as intended :P
            var deg2radians = Math.PI / 180;
            var rad = v * deg2radians ;
            var costheta = Math.cos(rad);
            var sintheta = Math.sin(rad);

            var m11 = costheta;
            var m12 = -sintheta;
            var m21 = sintheta;
            var m22 = costheta;

            var str = "progid:DXImageTransform.Microsoft.Matrix(sizingMethod='auto expand' M11="+m11+" M12="+m12+" M21="+m21+" M22="+m22+")";
            anim._node.setStyle( 'filter', str );
        }
    };
}, '0.0.1', { requires: ['anim'] });

Again, I have the full code I used up here, if you wanted to check out the oddness of IE as well ;)

Next Page »