Thursday, September 27, 2012

A Comparision of HTML5 Animations with SVG and CSS3

A big part of building a site can be spent coding animations.  Various Javascript libraries, like jQuery.animate(), have greatly simplified this process.  However, if you can do more with less code, why not do it.  That's what I was interested in determining with both the SVG animate tag and CSS3 transition style.  The question is how well its supported, what kind of control do you have over it, and most importantly, does it reduce the amount of code you need to write.

I decided to create a very simple progress bar implemented using jQuery.animate(), CSS3 Transitions, and SVG animate.  By far, the easiest to build was the SVG version.  If I didn't want to start/stop or know when it repeated, then no code would be required to execute the animation.  The following markup is sufficient:


<svg width="300" height="100">
<rect fill="red" height="50" width="50" y="25" x="5">
<animate id="svg-a1" attributeName="x" repeatCount="indefinite" begin="indefinite" values="5;53;101;149;197;245" restart="always" calcMode="discrete" dur="1500ms" fill="freeze"></animate>
</rect>
<rect stroke-width="3" stroke="dimgray" fill="none" height="50" width="290" y="25" x="5">
</rect>
</svg>


The animate tag provides the necessary description to move the progress indicator from left to right inside the container. If you wanted it to start immediately upon loading, you would remove the begin="indefinite" attribute and the animation will loop indefinitely.  With that attribute set, we're indicating that we will be triggering the start of the animation from code:


var svgcnt = 0;

$('#svg-a1').on('repeatEvent', function ()
{
$('#svg-repeat').html(++svgcnt);
});

$('#svg-start').on('click', function ()
{
$('#svg-a1')[0].beginElement();
});

$('#svg-stop').on('click', function ()
{
$('#svg-a1')[0].endElement();
});


The animate tag exposes begineElement() and endElement() methods to the DOM which allow you to start and stop the animation. I also hooked into the repeatEvent to maintain a loop counter. After testing in all the current browsers (other than IE), I found that Chrome does not support the event. Other than this one issue, this progress bar worked really well.

Moving on to the jQuery and CSS3 versions, the following markup is shared by both implementations:

<div class="jq-container">
<div class="jq-outer">
<div id="jq-progress" class="jq-inner"></div>
</div>
</div>


The jQuery version requires some extra code to create the step effect and loop the animation:


var jqcnt = 0;

var doAnimate = function()
{
$('#jq-progress')
.animate({tabIndex: 0},
{
duration: 1500,
easing: 'linear',
step: function (now, fx)
{
fx.elem.style.left = (47 * Math.floor(6.0 * fx.pos))+'px';
},
complete: function ()
{
$('#jq-repeat').html(++jqcnt);
doAnimate();
}
});
};

$('#jq-start').on('click', function ()
{
doAnimate();
});

$('#jq-stop').on('click', function ()
{
$('#jq-progress').stop(true);
});


Here, I used the step callback to do the math to create stepping effect of the progress bar and then the complete callback to update the loop counter and restart the animation.

Sadly, the CSS3 implementation was the most difficult. I used jQuery to set the CSS styles so it would use the correct browser flavor (ie -webkit, -o, -moz):


var c3cnt = 0;

$('#c3-progress').on('transitionend webkitTransitionEnd otransitionend', function ()
{
$('#c3-repeat').html(++c3cnt);

$('#c3-progress').css(
{
transform: '',
transition: ''
});

setTimeout(function() {
$('#c3-progress').css(
{
transform: 'translate(235px,0px)',
transition: '1200ms steps(5, start) 300ms'
});
}, 1);
});

$('#c3-start').on('click', function ()
{
$('#c3-progress').css(
{
transform: 'translate(235px,0px)',
transition: '1200ms steps(5, start) 300ms'
});
});

$('#c3-stop').on('click', function ()
{
$('#c3-progress').css(
{
transform: 'translate(0px,0px)',
transition: ''
});
});


I had to bind all the events to catch each browser specific event type - jQuery did not do that for me. The case of the event name is important and inconsistent between browsers, however, once I figured it out, all the browsers supported the event callback (unlike the SVG version). Additionally, I could not reset the animation and restart it without using setTimeout to delay it it by a millisecond. The other tweak which was slightly annoying was the necessity for the 300ms delay on the animation. Otherwise, the initial position would just jump and not match the other versions. The steps() function in the transition provides less control over incrementing of the transition over time compared to the SVG animate syntax.

One of my motivation of doing this test was to see if any of the animations would continue even if Javascript code was executing. The jQuery animation would not be able to continue, but since part of the CSS3 animation (other than looping it) and the SVG animation are all being handled by the browser so I thought maybe they'd keep running. I added a intensive loop to my demo to see what happened. All the browsers except Opera stopped all the animations. Opera continued to animate the SVG and the CSS3 transition (until it ended and needed to be looped via Javascript). I was quite disappointed to find that the animations are not being handled by the browser in a separate thread and therefore not affected by the Javascript execution. I understand there are interaction points available between Javascript and the DOM objects so thread synchronization is an issue but there seems to be an opportunity to provide significant performance increases to animations via the CSS3 and SVG features.

One last point to note is the CPU utilization across browsers. On my computer (which is quite old), Firefox ran just under 10% followed by Chrome around 7%, and Opera being the least intensive at 5%. I've tried to run some fairly complex SVG animations in Firefox recently and they have just crawled. This further illustrates that there is considerable room for optimizations.

Overall, the most powerful animation that does not require any code is SVG. There is a lot of flexibility available in the markup syntax. However, I do know there are also some inconsistencies between browsers. I did not find any with the animation I did except for the issue with the event triggering in Chrome. The CSS3 variation could be achieved with stylesheets which would reduce the code, but you need to duplicate the declarations several times to cover all the browser implementations. The available control is more limited than SVG, but basic transitions could be accomplished with very little code other than changing classes on an element. In the end, there are now quite a few alternatives to creating animation effects in the browser. As support becomes more consistent, it will be interesting to see what kinds of solutions are possible - both in performance and ease of implementation.

Wednesday, September 26, 2012

Using SVG Elements with jQuery

Exploring how SVG can provide some interesting enhancements to a page becomes more possible as support and consistency increase across browsers.  SVG elements offer a lot of flexibility for shaping content beyond the square box.   Of course, with the added features, some complexity is to be expected.  SVG adds a whole new lexicon to the markup language.  One that has a much steeper learning curve than normal HTML and its accompanying styles.  Because of this, libraries have been built to try to make it easier to dynamically create SVG objects and get them into the DOM.  Before jumping into one of these libraries, I typically like to see how to make something work so I can fully understand the advantages the library is offering.  Since I'm already using jQuery, I thought I'd start by seeing what can be done with jQuery alone.  As a reference, I decided to compare my results with the jQuerySVG plugin since it is suppose to enhance jQuery to provide SVG functionality.  Raphaël is another popular SVG library which has no other dependencies.   However, at this point, I'm most interested in working with jQuery and learning how much I can do with it.

As a starting point, I attempted to add a SVG circle to an existing svg element on my page:


var $svg = $('#mysvg');
$('circle').appendTo($svg);


However, nothing happened. As it turns out, you can't just use document.createElement() to add SVG DOM Nodes. You have to use document.createElementNS() which is something jQuery is not doing. I'm not quite ready to admit defeat here. $() accepts a DOM object, so I decided to write a little function that could take a SVG tag and use createElementNS() to create and return the element:


function SVG(tag)
{
return document.createElementNS('http://www.w3.org/2000/svg', tag);
}


Then, I can just place SVG() into the $() call to get a jQuery object. Notice that I did not even add the element to the DOM - just created an element to work around jQuery.


var $svg = $('#mysvg');
$(SVG('circle')).appendTo($svg);


Ok, this technically doesn't add anything visible to the page. However, there is an element at least in the DOM now. For this circle to look like anything, I need to set the required attributes for a circle:


var $svg = $('#mysvg');

$(SVG('circle'))
.attr('cx', 130)
.attr('cy', 75)
.attr('r', 50)
.attr('fill', 'none')
.attr('stroke', 'red')
.attr('stroke-width', 3)
.appendTo($svg);


I can set all the available options for the circle using the jQuery.attr() function. So, with only a minor work-around, I can dynamically create, modify, and add an SVG element to the DOM.

As a comparison, here is how you would do the equivalent with jQuerySVG:


var svg = $('#jqsvg').svg('get');
svg.circle(130, 75, 50, {fill: 'none', stroke: 'red', strokeWidth: 3});


Here, you need to first get a reference to the SVG object via the svg() plugin method. Then you can access the drawing functions to create a circle. If you want to set anything other than the object specific properties (in this case the circle's center and radius), you need to pass an object naming those specific options. In my opinion, your not really gaining anything so far. I prefer the explicit attribute names in my original example over having to remember what the first 3 parameters mean in the circle() call. It may seem overwhelming to learn all the possible tags and attibutes, but there is really good reference available at the W3 site

jQuerySVG exists for a reason, so I decided to try a more complex example. Just adding SVG objects to the DOM is clearly not enough. We want to try animate and hook event listeners to those objects.

I created a side-by-side example which illustrates adding a rectangle and animating its size and color. I also attached a handler to the click event to see how that works. What I found is jQuerySVG does provide some nice shortcuts for animating SVG properties. jQuery animate() won't do it automatically so you have to use the step callback to do the work yourself.

Without jQuerySVG's help:

$mr.animate({tabIndex: 0},
{
duration: 2000,
step: function (now, fx)
{
$(this)
.attr('width', 150 + Math.round(50 * fx.pos))
.attr('height', 25 + Math.round(25 * fx.pos)+'%')
// .attr('stroke', 'aqua') Need color transistion helper to make this happen
.attr('stroke-width', 3 + Math.round(7 * fx.pos));
}
});


Using jQuerySVG to animate properties:

$(sr).animate({
svgWidth: 200,
svgHeight: '50%',
svgStrokeWidth: '+=7',
svgStroke: 'aqua'
}, 2000);


As you can see, I have to do the math myself to animate the attributes and I can't really do the color animation without some helper logic to make it work.

One other caveat worth mentioning when working with jQuery and SVG is the case-sensitivity of the attribute names. The following animate tag will not work:


$(SVG('animate'))
.attr('attributeName', 'stroke')
.attr('dur', '2s')
.attr('begin', 'indefinite')
.attr('values', 'blue;aqua')
.attr('repeatCount', 1)
.attr('fill', 'freeze')
.appendTo($mr)


jQuery converts all attributes to lower case before setting them. Unfortunately, attributeName and repeatCount must maintain their case (at least in FireFox) to work. Anywhere there is a camel case attribute, you'll run into this problem. The circle tag used dashes to separate words (ie stroke-width) so I don't why there's an inconsistency other than animate is from the SMIL standards. To work around this problem, you'd have to call the DOM setAttribute() method directly on the animate object.

This exercise has shown me that I can use jQuery to work with SVG without any helper libraries. There are some clear limitations that need to be considered when taking this approach. A library like jQuerySVG does provide a means to abstract those details and add some clear convenience functionality (like animating SVG properties). Depending on the complexity of the project, using an SVG library may make sense for those reasons. However, if you know the limitations and are willing to work around them, you can get most of the functionality without any additional libraries.

Tuesday, September 25, 2012

Fast Javascript Animation using GreenSock's TweenLite Library

HTML5 compliant browsers are steadily replacing all the old non-compliant browsers.  Microsoft finally caught up with IE9 and you can start to count on the technology to be available.   Eventually, being forever backwards compatible with the old will be a thing of the past.  As that happens, we want to see what our new toys can do and how far we can push them.  One area that will see change is moving from Flash-based animations to HTML5-based animations.  However, for that to happen, browsers and their supporting libraries will need to be able to match the performance and functionality of their Flash counterpart.

If you've spent anytime with jQuery, you may have tinkered with the animation engine to perform various effects on the elements on your page.  If you've really played with jQuery, you might have tried to coordinate the animation of a pile of elements on your page only to be disappointed by the performance.  Luckily, this is not an inherent issue with browser technology, its just a matter of using the right library for the job.  jQuery's animation engine does very well with basic transition effects.  However, if you trying to build the next great game using all the latest HTML5 goodies, you're going to need something a bit more robust.  This is where GreenSock's TweenLite library comes in.  Built specifically for animation, this tool has a nice array of features to make animating a breeze.  In one line, you can animate any element on your page:


// myBox is a div that is styled to be 80px wide and 50px tall

TweenLite.to(document.getElementById('myBox'), 2, {css: {width: 150, height: 100});


Over the course of 2 seconds, my box will go from 80x50px to 150x100px.  The library supports a nice array of easing functions, start/step/stop callbacks, and various other playback control method.

If you'd like to use jQuery with TweenLite, its very simple - instead of getElementById(), you can use a selector:


TweenLite.to($('#myBox'), 2, {css: {width: 150, height: 100});


Notice that you can pass the jQuery object and TweenLite understands how to use it.  If you want to change the animated variable on a collection of items, you can use $.each to run the animation on each element:


$('.box').each(function (i, el)
{
TweenLite.to(el, 2, {css: {width: 50*(i+1), height: 100});
});


Here I changed the final width of each element based on its index in the collection. The first element will animate to 50px, the second to 100px, and so on. You can get a lot more fancy than that - I created a demo in my sandbox that illustrates the use of several callback functions, CSS3 transforms, and most of the easing functions.

In summary, here's my take of the library:


  • Fast (see the speed test) - if your project has a lot of things moving, this library can keep everything going nice and smooth.

  • Small - everything is modularized so you can get started with only a 9Kb footprint.  From there, you can add in the pieces you need.  The CSS plugin does add another 16Kb so if you're just doing a few basic CSS animations, you might consider animating a dummy property and use the onAnimate callback to set the styles directly on the element

  • CSS3 Transform support - the CSS plugin handles all the CSS3 transform function like rotate, skew, etc.  In jQuery, you would need Transit and then use the transition() method to do the same.

  • No dependencies - no other library other than plain old Javascript are needed.

  • Roots in Flash - GreenSock has been building ActionScript libraries and all the familiar concepts of animating in Flash are here like timelines, play head, etc.  If you're making the transition from Flash to HTML5, TweenLite will have a similar look and feel.

  • Not MIT/GPL licensed - pay attention to the terms of use before building something with the library.  For the most part, you can use it for free. Just make sure you're inside the bounds of the terms, otherwise, you'll need to buy a license. However, its a reasonable fee and may be worth the investment regardless of how you use it because of some of the extra goodies you get.

  • Really new - the Javascript version of the library just came out in v12 which is still in beta.  However, since its based on the ActionScript versions, it should be fairly stable.

  • Not a jQuery plugin - if you're looking for a jQuery.animate() equivalent, this is not implemented that way. However, the approach provides much greater control and versatility than you might be able to achieve when using the jQuery plugin approach. This becomes most apparent as you start using the TimelineLite object.



Sometimes using the right tool for the job is a necessity.  TweenLite is only an animation engine.  It doesn't try to be anything else.  While other libraries that provide DOM selecting and traversal (like jQuery) do have built-in animation capabilities, some projects will need the enhanced speed and features of TweenLite to really leverage the full capabilities of the browser. This is really a great library that I have only started to tinker with. I'm excited to try out some of the other features it offers and will post some demos as I continue working with the library.

Monday, September 24, 2012

Aligning DOM Elements around a Circle

I've been working on a project that requires elements to be positioned relative to an imaginary circle.  I need to be able to support two different alignment scenarios.  Both of these are illustrated below:

                   

I've added the imaginary circles as a reference for the alignment:

                   
I decided to start by figuring out where I needed to align my elements.  We can any point on the circle using the following formula:




(x',y')=(cx+r*cos(α),cy+r*sin(α))


Where cx and cy are the center of the circle, r is the radius, and α is the angle of the point, in radians, relative to the center of the circle.

In my example, I want the elements to be evenly distributed around the circle.  I have five elements so each element should be located 1/5 the way around the circle.  360 / 5 = 72 so the first element will be place at the point 72 degrees around the circle, the second element at 144 degree around, and so on.  If I plug my numbers into the above equation and set the top/left of each element, I get the following:



That's a good start but the elements are suppose to be on the perimeter of the circle and aligned to their middle/bottom point.  If we back to the original reference and look at Item 2 which positioned 72 degrees around the circle moving clockwise from the top, you can see that rotating the element by 72 degrees will make it tangent to the circle at the same point:



In jQuery, I can set this via the css() function:

          $el.css({
                transformOrigin: 'top left',
                transform: 'rotate(72deg)'
             });


If you noticed, I also set transform-origin to "top left" to ensure the visual top/left corner of the element shared the top/left corner in the DOM.  This is important so aligning does not become tedious.  Now we have the elements rotated but not positioned on the circle at the bottom/middle point:



We need to perform one more transform to move the element's bottom/middle point to the top/left point.  Again, we can use CSS3 transform functions:


$el.css({
                transformOrigin: 'top left',
                transform: 'rotate(72deg) translate(-40px, -50px)'
             });


The translation assumes my element is 80px width and 50px tall.  The middle is at 40px and the bottom is at 50px relative to the top/left.  By combining the two transform functions, we get the final alignment:



The second alignment scenario is a little bit trickier.  Its not as easy as finding the point on the circle, aligning the top/left, and performing a transform on the element.  Instead, as each element is placed around the circle, the point touching the circle is different and the distance from the center of the circle to the top/left is different.  While a mathematical solution may be possible, I chose a slightly different approach



If you draw a path through the top/left points of each element, you will notice they form a rounded box.  In fact, it is simply a rectangle with a width of 2*r+w and a height of 2*r+h with a corner radius of r (r is the radius of the circle, w/h is the width/height of the element).  If you align this rounded rectangle so the bottom/right aligns with the bottom/right of the circle, you can align the element's top/left point to points along the rectangle's perimeter and the elements will line up properly:



I can find a point on the rectangle by moving along its perimeter clockwise the same relative distance I would position the element along the circle.  To do this, I use the expected angle divided by 360 degrees to find the percentage of distance along that path.  I can then find that point on the rectangle and position the element appropriately.  I actually used a library I built called PathJS to locate the correct point.  PathJS can generate interpolated points along any number of paths.  The points are sequential so you can actually step through them to do things like path animations or, in this case, estimate the position of a point on the perimeter.

I created a small demo in my sandbox that allows you to toggle parts of the alignment on and off.  It also demonstrates how the elements can be further animated along the path or aligned in several different configurations.

Friday, September 21, 2012

Animating Multiple Elements Simultaneously with jQuery

Consider a group of absolutely positioned divs that are visually styled to look like 50x80 pixel rectangles (I setup a full demo in my sandbox to illustrate):


<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>


We can animate all of them at once using a class selector and calling animate:


$('.box').animate({left: 100, top: 100}, 'slow');


This will move all the boxes to the same location of the page. Unless the elements were all in different places to begin with, this animation will look rather unimpressive.

We can't select all the elements and set different target CSS properties so we need to calculate them and then iterate over all the elements using each() to animate each box individually:


/*
var sp = starting point of divs
var ep = 5 different end points
*/

// Animate all the boxes out to 5 different top/left positions
// All of the boxes start are the same point
$('.box').each(function (idx, box)
{
var $box = $(box);
$box.animate({left: ep[idx].x, top: ep[idx].y}, 2000, 'easeOutBounce');

});

// Return back to the starting point
$('.box').animate({left: sp.x, top: sp.y}, 'slow');


So one question comes to mind when looking at this code:

Why did all the animations start at the same time? Shouldn't we expect some slight discrepancy in timing because they can't all be started together?

The answer is that Javascript runs in a single thread. None of the animations can occur if something else is executing. This is both helpful and problematic. Its helpful because we can leverage the single thread to setup all of our animations so they start at the same time. Animate() is just adding to a queue and returning. The animation process can't start until our setup is complete. Its problematic because if some other code is running when the animation is suppose to be running, jQuery will skip ahead giving the appearance of choppy animations.

In the demo, one of the examples has a for loop that iterates 100,000,000 times after each element's animation is queued. By time the code finishes, the animation basically jumps to the end. This is because as soon as you call animate(), the clock starts ticking, jQuery will ensure the animation runs for the amount of time requested. If you say 500ms and your code takes that long to finish running before relinquishing control, the animation will just jump to the end. This means that you have to attempt not interfere with execution because you will starve the animation code from getting time to run.

To avoid crummy animations, we can adopt several strategies:

  1. Ensure you pre-calculate everything the animation will need to do. This way the complexity around the actual animate() calls will be minimal and the least amount of time necessary will be used to queue all the animations. If you use the step callback option in animate(), ensure that this code is using pre-calculated values as well to augment the animation.

  2. Consider disabling event handlers to cut down on unexpected code execution. Anything the user can click, drag, etc. will all steal from the animation execution and potentially affect the smoothness of the animation effect


When I first started this exercise, I was afraid it would be difficult to synchronize the animations of all the elements. I had forgotten about how the single thread would affect the execution. This "feature" of Javascript does simplify getting everything lined up as long as you are also aware of the limitations and work around them accordingly.

Wednesday, September 19, 2012

Extending jQuery Animate() Beyond Basic CSS Animation

Using jQuery to enable DOM animation is very simple. The library does an excellent job of hiding all the details of managing timing and pace for you. The core animate() function is capable of handling most numeric CSS properties. However, I was interested in how you might extend the animation library to enable working with other CSS (or even non-CSS) properties. The jQuery documentation does not provide any details about extending the animation engine. So, I figured that I would have to dig through the code and spend some time researching what others had done:

http://onwebdev.blogspot.com/2011/02/jquery-fx-object.html - Talks about the jQuery animation internals. The code snippets seem outdated because the current code base doesn't look the same any more. However, you can get the basic point.

http://cdmckay.org/blog/2010/03/01/the-jquery-animate-step-callback-function/ - Discusses the FX object properties and what they represent.

Now that I have a better understanding of the internals, I decided to look at some actual implementations:

http://playground.benbarnett.net/jquery-animate-enhanced/ - Uses CSS3 Transitions to animate top/left CSS properties and opacity.  Proxies both animate() and stop(). Since this enhancement is intercepting properties already handled by jQuery, the proxying seemed appropriate. However, I'm more interested in adding properties not handled by default.

https://github.com/weepy/jquery.path - Adds a custom function to jQuery.fx.step to perform the custom animation along non-linear paths. The actual extension is quite simple:


$.fx.step.path = function(fx) {
var css = fx.end.css( 1 - fx.pos );
if ( css.prevX != null ) {
$.cssHooks.transform.set( fx.elem, "rotate(" + Math.atan2(css.prevY - css.y, css.prevX - css.x) + ")" );
}
fx.elem.style.top = css.top;
fx.elem.style.left = css.left;
};


http://www.bitstorm.org/jquery/shadow-animation/ - Adds to $.Tween.propHooks to enable animation of the boxshadow style. The $.Tween object seems to be relatively new and in jQuery 1.8, you can see that $.fx is now equal to the tween prototype.

Finally, I decided to see how the jQuery UI color animation worked:


jQuery.fx.step[ hook ] = function( fx ) {
if ( !fx.colorInit ) {
fx.start = color( fx.elem, hook );
fx.end = color( fx.end );
fx.colorInit = true;
}
jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
};


It seems that adding to the jQuery.fx.step object is currently the best approach.

So the next step is to test this out with a simple example to see it work. I decided to extend animate to allow animating a "size" property. This is not really necessary since I can simply animate height and width just fine. But its really simple and removes any extra code that would be necessary to implement something more complex. The goal is to see how it works. I'd like to trace through the code paths to see where and when certain things happen so I can understand how I might build a more useful extension.

I'd like my "size" animation to be able to accept a point to expand/contract around. This is similar to setting the transform-origin and using the transform scaling function. However, I'm going to implement it by adjusting the element's top/left to make it appear to be sizing around that point.

The implementation of the size property animation is below:

$.fx.step['size'] = function(fx)
{
if ( !fx._sizeInit )
{
var $el = $(fx.elem),
c = fx.end.center || {top: 0, left: 0};

fx.start = $el.offset();
$.extend(fx.start, {width: $el.width(), height: $el.height()});

fx._sizer = {};

fx._sizer.topDelta = c.top - (c.top * fx.end.height / fx.start.height);
fx._sizer.leftDelta = c.left - (c.left * fx.end.width / fx.start.width);
fx._sizer.widthDelta = fx.end.width - fx.start.width;
fx._sizer.heightDelta = fx.end.height - fx.start.height;

fx._sizeInit = true;
}

fx.elem.style.top = fx.start.top + Math.floor(fx._sizer.topDelta * fx.pos) + 'px';
fx.elem.style.left = fx.start.left + Math.floor(fx._sizer.leftDelta * fx.pos) + 'px';
fx.elem.style.width = fx.start.width + Math.floor(fx._sizer.widthDelta * fx.pos) + 'px';
fx.elem.style.height = fx.start.height + Math.floor(fx._sizer.heightDelta * fx.pos) + 'px';

}


There are several key aspects to note:

  • fx.elem is the DOM node not the jQuery object.

  • You can add properties to the fx object and use them in subsequent calls to the step function. The object persists between calls to the step function.

  • All the calculations have been done on the first step and saved so all future calls do the least amount work possible. Trying to do too much work on each animation step risks dropping frames in the animation which will make it look choppy. This is further illustrated by directly setting the style attribute on the element instead of using the jQuery css() function.



To animate this property, you would only need to give the "size" property in the animation call an appropriate object:


// myDiv is a 50 x 60 box. This will animate the size
// to 100 x 200 relative to the top/middle of the box
$('myDiv')
.animate({
size: {
center: {top: 0, left: 30},
height: 100,
width: 200
}
}, 'slow');


I created a demo that uses the above animation plugin to resize a box. The center coordinates are relative to the top/left of the element. Try entering a negative origin or an origin outside the boundary of the box. Essentially, all this size property did was do the math to find the different deltas - the harder part was move the top/left corner around to make the animation appear to be occurring around a different origin point. Like I said before, you could simply animate the transform scaling function with an appropriate transform-origin point set. jQuery animate can not do that natively but the Transit plugin extends jQuery to enable this functionality. However, if your interested in support, the approach I presented above will be backwards compatible with older browser versions.

I purposely kept this example simple so it was easy to see how to augment the animation fx object to create a new property handler. Some useful additions to this code would include relative resizing, percentages, etc. All of those require more parsing and logic which would detract from the example. As you can see, extending the capabilities of jQuery.animate() is relatively easy and enables building powerful extensions.

Tuesday, September 18, 2012

Javascript DOM Animation Performance - Speed Comparisons

I've been working with animating using jQuery for a while now and have been struggling with dropped frames and general choppiness. I've also seen others having similar issues - especially on mobile devices. I found this speed test on GreenSock's site and the results are quite remarkable. By far the most noticeable was when I was using my tablet and the 300 object animation in jQuery didn't move yet in TweenLite worked at rather smooth pace of 23 fps. The other notable contender was Zepto - a jQuery clone - which appears to be targeted more to mobile devices and doesn't even support IE.

It tells me that complex animations in the browser are possible. It doesn't mean jQuery's animation engine is bad. It works great for basic effects like fading in/out or sliding in/out of several elements. In fact, I don't think the library makes any claims to be the fastest engine out there. Part of jQuery's draw is its ability to normalize browser support. The tradeoff for that feature will be certainly be animation speed. Its probably way, in part, Zepto performs so well. Although, I'm sure some optimization in jQuery's implementation is always possible.

So, if you're planning to do more complex animations over a large set of elements, then you may want to look at one of these other animation engines. Since TweenLite is only an animation library, it might make sense to use it for those situations in tandem with jQuery's excellent DOM features.

Monday, September 17, 2012

Using Password Protected Keys in Linux with SSH-auth

Passwords are great until you have to type them over and over again. I recently started using SSH keys with GitHub and, like a good user, put a password on my private key. That unfortunately requires me to type my password far too often. Once would make me much happier so I decided to find a solution which will allow me to continue being lazy.

I use Pageant in Windows so I knew something existed - it was a matter of finding out how to set it up. A quick read of various GitHub help articles got me to the point where I could test my connection and verify it was using my key file (I'm assuming you've gotten this far as well).


ssh -vT git@github.com


I saw the reference to ssh-agent and ssh-add and thought I had my solution. So I ran ssh-agent and then tried to add my key via ssh-add and received the following error:


Could not open a connection to your authentication agent.


Some time later, I found out that the stuff ssh-agent spits out when it starts up is important. Its actually commands you should run to set your environment variables. Ok: copy & paste, run ssh-add with my key, enter my password, and try to connect to GitHub.

Works! Wonderful - now that was too many steps. Again, chronic laziness needs to be maintained. You can eliminate the copy & paste by using:


eval `ssh-agent`


That will execute the echoed bits from ssh-agent. But, again, I really want this simple so I don't have to try to remember anything. So why not make a shell script to run both the ssh-agent and ssh-add commands? Well, that shell script will run in a separate process space and everything that happens there will not be in your process space when its done.

The solution is to use a shell function. Just add something like this to the bottom of your .bash_profile (or equivalent):


function authsshkey()
{
eval `ssh-agent`
ssh-add .ssh/id_rsa
}


Now, from the command line (after running . ~/.bash_profile to load the changes), type:


authsshkey


And you'll be prompted for you password. Now when you run:


ssh -vT git@github.com


You won't be asked for your password. Each time you login to your shell, you'll need to run authsshkey to get your key into memory. However, its down to one short command and one time password entry and then you're free from all the extra typing while in that session.

Laziness restored!

Friday, September 14, 2012

Path Interpolation Using Cubic Bezier and Control Point Estimation in Javascript

Let's say I have a series of individual points and I want to find a smooth path through them from start to end:

[caption id="attachment_243" align="aligncenter" width="500"> Set of points P0-P8[/caption]

Without any other information, I'd like to create a new semi-continuous path of points that smoothly runs through each of these key points in the defined path:




[caption id="attachment_242" align="aligncenter" width="500"> Key points anchor the line which smoothly winds through each one[/caption]

Immediately, a Bezier function of some sort seems appropriate to find this path.  An initial glance at the math to find this path through 9 points seems daunting.  Every site I looked at had long sets of calculations, proofs, and other notations which were not answering my question.  Fortunately, after some digging, I came to the conclusion that I did not need to find a curve through all the points at once.  It is possible to construct smaller curves point-to-point which will result in one larger smooth Bezier curve (Wikipedia).  In that case, we need at least three points to find a curve.  However, a quadratic Bezier (three points) does not have an inflection point so our curved segments would not flow nicely together.  Instead, we need to go one order higher to a cubic Bezier (four points) so we can build an "S" shaped segment.  With that concept in mind, we should be able to iteratively build cubic Bezier curves using several points at a time and then link these individual curves together end-to-end to form one continuous path from P0 to P8.

The first challenge to solve is how to find control points the Bezier function needs to calculate the curvature.  The points we're feeding into the algorithm are only anchor points.  However, each segment needs two anchor points and two control points.  After some research, I found a nice reference that showed how to construct triangles and midpoints from the anchor points to infer the control points.  As I was working on a demo, I got a little confused because in my mind I was trying to find four points to use in the cubic Bezier function and was calculating triangles from three anchor points which created two control points around the second point in the triangle ... etc.  Needless to say, there are a lot of points.  So with my trusty pen and paper, I set out to label all the points and attempt to make sense of them all.  When all was said and done I ended up with the following basic approach:

  1. Working with four sequential points at a time (P0, P1, P2, P3) from our total list of points do the following

    1. Find two control points (C1 and C2) based on the first three points (P0, P1, P2).

    2. Find two more control points (C3 and C4) based on the last three points (P1, P2, P3).

    3. Find the cubic Bezier curve for P1 and P2 using C2, C3 as the control points.

    4. Return the interpolated set of points between P1 and P2.



  2. Append the returned curve segment to the end of the prior segment and shift forward one point and repeat.


Once this process is complete, you will have assembled a curve that runs through all the points.  Each curve segment requires four points to construct.  These four points are not the inputs to the cubic Bezier function.  Instead, they create two triangles that are used to find the two points control points that will be used in the Bezier function.  Each triangle yields two control points, but only one of those points per triangle is used per segment (they actually all get used, the other control points that are calculated are for the segment before and after the segment being built.  It was just easier to throw it away and recalculate it later than trying to store it and recall it in subsequent iterations of the algorithm).  For any segment, you need to know the point before and the point after so you can find the correct curvature that will allow each curve segment to flow together seamlessly.

There are several observations you might make when looking at this algorithm:

  1. There is no point before the first point nor is there a point after the last point so how does the first and last segment get calculated.  The solution I came up with was to duplicate the first and last points and add it to the front and end of the list of points, respectively.  This creates a zero length leg on the triangle which creates a zero length control point which finally creates a zero length first/last segment.  However, the rest of the segments can be calculated properly because we have these fake anchor points.

  2. Every segment does a lot of math.  A creative solution is needed to find an algorithm to calculate the cubic Bezier with the least amount of math in the iteration steps.  I found one that only does addition in the looping section by calculating various derivatives prior to the loop.  This site has a nice explanation of the math if your curious.  The control point calculation algorithm also avoids using trigonometric functions to find the tangents to the the curve by using basic midpoints, proportions, and point translations.


Hooking all these pieces together I built a demo which allows you to plot some points on an HTML5 Canvas.  As you do this, the Bezier curve is calculated and each segment is drawn.  The curve is always one point behind since you need an extra point to calculate the prior curve segment (unless you click the "New" button which will complete the current curve by pushing a copy of the last point onto the end of points and run the calculation on the final segment).  The example also draws the various in-between points calculated to find the required control points.  I drew an example using the demo and labeled the various points to explain the process:

[caption id="attachment_249" align="alignnone" width="340"> The first point.  P0' is the copy of P0 which will allow the first segment to be calculated.[/caption]

[caption id="attachment_250" align="alignnone" width="340"> Add P1 - only three points so far - not enough to find a segment yet.[/caption]

[caption id="attachment_251" align="alignnone" width="340"> Add P2 - now there are four points so we can find the first segment.[/caption]

Above, you can see that after the first four points are plotted, we can find the first curve segment.  For the first iteration, you can only see one triangle (yellow lines).  This is the second triangle formed by P0,P1, and P2 (the first triangle is P0', P0, P1 but since the length of (P0',P0) is zero, that leg is not visible.  Notice that both triangles share the line (P0,P1).  The midpoints (M0,M1) are found on each line and the point Q1 is found on (M0,M1) by using the proportion of  the length of line (P0,P1) compared to the length of line (P1, P2).    The green line that connects M0 and M1 represents the tangent of the curve at P1.  If we translate M0 and M1 relative to the distance between Q1 and P1, we get the control points C1 and C2 which forms the red tangent line that runs through P1 (C2 is not shown here because its thrown out on this pass).  Notice how the length of (C1, P1) is the same as (M0, Q1).  We now have three of the points we need to find the curve - P0, P1, and C1.  But what about the other control point?  C0 is also calculated but since this is the first segment and P0' is the same as P0, it will be equal to M0.  So the first Bezier curve segment is found by (P0,C0,C1,P1).

[caption id="attachment_252" align="alignnone" width="340"> Add P3[/caption]

Now you can better see how the different control points are found.  For the curve between P1 and P2, we need C2 and C3.  Notice how C2 completes the other side of the tangent line through P1 and is just a translation of M1 relative to the vector between Q1 and P1.  So in this iteration, the control points C1, C2, C3, and C4 were all found, however, C1 and C4 are thrown out because they are not needed to find the curve between P1 and P2.  However, C2 and C3 can only be found by finding M0, M1, and M2.  We need those points to find Q1 and Q2 so the correct translation can be found to "copy" M1 to C2 and M1 to C3.

[caption id="attachment_253" align="alignnone" width="340"> Add P4[/caption]

Now I want to finish my curve.  However, I need a point after P4 to find the curve between P3 and P4.  The solution is to add P4 again:

[caption id="attachment_254" align="alignnone" width="340"> Close Line - add P4' to allow final segment to be calculated.[/caption]

You might notice that the plot points in the curves have a tendency to spread out and clump together.  The number of points to find is based on the distance between the anchor points.  This means that if the distance between point A to B is small and the distance from B to C is large, you will get points closer together between A and B and further apart between B and C because the number of points calculated is the same but spread out over two different distances.

Hopefully, this demonstration was clear enough to illustrate the process of building an interpolated curve using the cubic Bezier function.  I know it took me some time to rationalize all the calculations and points being created before understanding the what each piece represented and meant.  The efficiency of the algorithm compared to using trigonometric functions and many multiplications in the iterator makes it an excellent tool for quickly finding paths to use in various time critical tasks like animations.  This algorithm can even be used to draw sinusoidal shapes without using sine or cosine.  A few improvements I might investigate is controlling the step counter to make more even sets of points and allow control over scaling the control points so you can make smoother/sharper curves.  However, this is a pretty good starting point for the applications I have in mind for the function.

Thursday, September 13, 2012

CSS3 Transforms and Aligning DOM Elements

In a previous post, I did some experimenting to determine how using CSS3 transform rotations interacted with jQuery and DOM positioning. Based on those tests, I decided to move on to determining how to precisely align these elements to other elements on the page. The first observation I made when working on that test was that CSS3 transforms occur in a completely separate coordinate space than the DOM coordinate space. The ramifications of this behavior is that transforms can occur independently of other DOM positioning without affecting each other.

For instance, consider the following transform of two different elements. Both the red box and the blue box are rotated using the CSS3 transform rotate function. The black axis is the DOM axis and the red/blue axis are the transform axis for each box respectively. In this case, the origin of the transform is set to top/left. You can see that the two boxes are aligned so their top/left corners are at the (0,0) point of the transform axis. This point also corrisponds to the (0,0) point of the DOM axis since it also uses the top/left as the origin point.



However, you can change the origin point in the transform space to any other point on the element. In the following example, the bottom/right is the origin and the same rotation as above is performed. Notice the difference in the position of the elements relative to the position of the transform axis relative and the transform axises relative to the DOM axis.



Notice that the top/left corner in the DOM of both the first example and the second example are the same. When I realized that, it become evident that using the same point of origin in my transforms as the DOM would make position much easier.

Going back to using top/left origins, we can continue to investigate aligning the two elements relative to each other. By default, they are aligned at the top/left corner - but what if we want to align the top/middle of the red box with the bottom/left of the blue box. I've added those point in the image below.



There are several ways to align these points. I've created a series of tests in my sandbox to illustrate the approach and provide working code that will accomplish it.

The problem the different axis present is that DOM positioning occurs relative to the DOM axis. The transformed elements retain their original DOM top/left point on the DOM axis regardless of where the transformed element visually appears on the page. This is because the transform axis is independent of the DOM axis. This is a great feature as long as you are aware of its impact on the other CSS positioning styles. If you rely on absolute positioning and utilize transforms to manipulate elements, you will have to account for it either in the DOM positioning or the transform positioning.

This is where the examples lead - the last two lines show two different methods to handling positioning transformed elements. You can either calculate the transform that is occurring via the CSS yourself and account for it in the DOM positioning or you can use additional transform functions to manipulate the position of the element inside the transform coordinate space so the element is positioned properly relative to the DOM space.

The method you use is based entirely on what you're trying to accomplish. From my perspective, I'd say if the transform is basically static, then use the latter approach. If you need to animate or dynamically move the element around a path, than the former may be a better approach so you can pre-calculate the transforms to optimize the motion.

Wednesday, September 12, 2012

Using Bresenham's Algorithm to Create Shapes in Javascript

Until recently, there has been little reason to spend much time building graphic libraries for Javascript. However, now that HTML5 is becoming supported in basically all modern browsers, Canvas tags and CSS3 transforms have opened the door to delving into these concepts.

I want to work with non-linear objects like arcs, circles, ovals, etc. However, I'd like to avoid all the trigonometry and floating point calculations. We can approximate these points by creatively iterating with integers by using Bresenham's algorithm to generate each point in the path.

I found a C implementation of this algorithm for lines, circles, ellipses, and bezier curves and ported it to Javascript. Instead of plotting the points, I built an array and returned the set of x/y coordinates that represent the shape. I plotted an example of each shape on a canvas to see what it looked like. As it turns out, the algorithm can generate a lot of points so I decided to add a step variable to skip every N points. This can greatly reduce the number of points returned which is more than enough for the kinds of tasks I have in mind for these generated objects.

With some of these tools, I can now investigate all kinds of other fun things like non-linear path animation, hits areas that are not boxes, etc. I know that as more and more developers dig into building with the capabilities of HTML5, some really powerful libraries will evolve.

Tuesday, September 11, 2012

CSS3 Rotations: Where in the DOM is the Element?

I've been attempting to rotate DOM elements and position them relative to other DOM elements. I wanted to make sure I knew jQuery would position things based on the same point I was using and found that there are several different behaviors to take into account.

I generated several examples in my sandbox as a reference. Using one of the examples to refer to below, here's how it was generated and what each object represents. (The example pulls from the jQuery CDN so will always be the current version. As of this writing, that is 1.8.1). The examples were also tested in all the current versions of IE (9 and 10), Firefox, Chrome, Safari, and Opera and all produced the same results.


Using this image as a reference

First, create a reference axis and center the blue box at this point.

Next, use jQuery.offset() to retrieve the top/left of the blue box, rotate the red box, and then set the red box's top/left to the blue box's top/left:


pos = $bluebox.offset();

$redbox.css({
transformOrigin: 'bottom right',
transform: 'rotate(27deg)'
})
.html('rotated');

$redbox.css({
top: (pos.top) + 'px',
left: (pos.left) + 'px'
});


Now, add in some reference points we can study. The orange dot is where jQuery.offset() reports the top/left position of the red box.


pos = $r.offset();

$('<div>').appendTo($q)
.addClass('box j')
.css({
top: pos.top + 'px',
left: pos.left + 'px'
});


The green dot is positioned based on the DOM offsetTop/offsetLeft properties of the DOM node.


pos = {top: $redbox[0].offsetTop, left: $redbox[0].offsetLeft};

$('<div>').appendTo($q)
.addClass('box d')
.css({
top: pos.top + 'px',
left: pos.left + 'px'
});


And the black-border box is drawn to match the getBoundingClientRect() of the redbox.


box = $redbox[0].getBoundingClientRect();

$('<div>').appendTo($q)
.addClass('box c')
.css({
top: box.top + 'px',
left: box.left + 'px',
height: box.height + 'px',
width: box.width + 'px'
});


If you inspected the DOM and looked at the resulting HTML, it would look something like this:



<canvas height="200" width="200"></canvas>

<div style="-moz-transform-origin: right bottom; -moz-transform: rotate(27deg); top: 691px; left: 234.5px;" class="box r">rotated</div>

<div style="top: 691px; left: 234.5px;" class="box a"></div>

<div style="top: 660.133px; left: 243.217px;" class="box j"></div>

<div style="top: 691px; left: 235px;" class="box d"></div>

<div style="top: 660.133px; left: 243.217px; height: 80.8667px; width: 93.9833px;" class="box c"></div>



Notice how "box r" and "box a" have the same top/left styles but, visually, the top/left corners are not aligned.

Here are several observations:


  1. When positioning any rotated element relative to another element, the top/left stays the same as the unrotated element. If you refer to the examples, you can see that if you did not rotate the red box, its top/left corner will align with the blue box's top/left corner. The only time the top/left point of the rotated element will touch the blue box is when the transform-origin is set to "top left" (this is the second row in the examples).

  2. jQuery uses getBoundingClientRect() in the offset() function to find the top/left position of an element (the position() function uses offset() so it will result in the same behavior). This approach will result in a point not on the element nor will it be the same as what the browser will use when setting the position of the rotated element via CSS styles. Using the DOM offset* properties will, however, provide the same coordinates as the CSS styles. This means you must use care when using jQuery's offset/position functions on rotated objects. The point you get back will not be something you can use in a css() call to position the element (unless you want to position things relative to the rotated element's bounding box). You can use offset() to set the top/left corner of the element's bounding box relative to other items (see that demo here)
  3. It seems that rotating an element relative to its top/left corner will make it easier to position relative to other elements because the top/left corner will be fixed in the rotation and will visually be the same top/left on the rendered page as it is in the DOM. If you want to rotate around a different origin point, you'll have to do some math to find the visual top/left of the element so you can position it accordingly. The default origin (at least in FireFox) is the center of the element. You have to explicitly set transform-origin to "top left" to achieve that behavior.




Based on these experiments, I now know what to expect when positioning rotated elements. I don't consider any of the above behaviors wrong - I just have to be aware how everything behaves and design my solution accordingly. The approach you take is based on what you need to accomplish. There are many different positional values to manage once you start rotating things around on the page. Knowing what the browser and libraries are doing can save a lot of headaches as you design your site.

Monday, September 10, 2012

Diving into Matrices with CSS3 Transforms and jQuery

jQuery makes it easier to set the CSS3 transform style because it normalizes all the different browser specific variations for you. All you need to do is specify "transform: function(s)" and it does the rest. You can specify any set of functions you like: rotate, skew, translate, etc. However, if you get the transform style with jQuery.css(), you will always get a matrix transform back.

Consider the following code snippet:


<body style="margin: 10px">

<div id="myDiv">rotate this</div>

<br/><br/>

<pre>
<script>
$(function()
{

$('#myDiv').css({
transformOrigin: 'top left',
transform: 'rotate(27deg)'
});

$('pre').html($('#myDiv').css('transform'));

});

</script>
</pre>

</body>


When executed, this is what you will see:



This means that if you're relying on setting the CSS and then retrieving it later to modify it, you need to work with matrices. Your only other options are to maintain other variables, use data(), or store attributes on the elements via attr(). These are all less eloquent ways of working around the problem - the information is already stored on the element, you just need to work with the values as they are natively being represented.

Javascript does not have any built-in matrix functions. However, there are some libraries out there that provide the functionality. Matrix math alone is not enough to manage the transform style - these are specially crafted matrices that leverage the properties of linear algebra to perform these calculations. This isn't a new concept - 3D graphics have been doing this for a long time - we just get to use it in HTML now.

So I did some searching and found the W3 SVG spec which outlines how to construct all the different 2D transform matrices. Additionally, I found Sylvester to represent the matrices and do the actual math. Using this information, we should be able to build some simple extensions to build a transform, convert it to a CSS transform style string, and then do that in reverse to perform additional transforms.

First, we need to define some transform "recipes" that are matrices that can be combined together to form a final transform:


var _T = {
rotate: function(deg)
{
var rad = parseFloat(deg) * (Math.PI/180),
costheta = Math.cos(rad),
sintheta = Math.sin(rad);

var a = costheta,
b = sintheta,
c = -sintheta,
d = costheta;

return $M([
[a, c, 0],
[b, d, 0],
[0, 0, 1]
]);
},

skew: function(dx, dy)
{
var radX = parseFloat(dx) * (Math.PI/180),
radY = parseFloat(dy) * (Math.PI/180),
c = Math.tan(radX),
b = Math.tan(radY);


return $M([
[1, c, 0],
[b, 1, 0],
[0, 0, 1]
]);
},

translate: function(x, y)
{
var e = x || 0,
f = y || 0;

return $M([
[1, 0, e],
[0, 1, f],
[0, 0, 1]
]);
},

scale: function(x, y)
{
var a = x || 0,
d = y || 0;

return $M([
[a, 0, 0],
[0, d, 0],
[0, 0, 1]
]);
}
};


Here I've used Sylvester to represent the matrices. The Matrix class stores the matrix as a series of arrays. The class provides functions to perform matrix operations on these representations.

Next, we need to be able to convert from the CSS style string to a matrix and back again:


toString: function (m)
{
var s = 'matrix(',
r, c;

for (c=1;c<=3;c++)
{
for (r=1;r<=2;r++)
s += m.e(r,c)+', ';
}

s = s.substr(0, s.length-2) + ')';

return s;
},

fromString: function (s)
{
var t = /^matrix\((\S*), (\S*), (\S*), (\S*), (\S*), (\S*)\)$/g.exec(s),
a = parseFloat(!t ? 1 : t[1]),
b = parseFloat(!t ? 0 : t[2]),
c = parseFloat(!t ? 0 : t[3]),
d = parseFloat(!t ? 1 : t[4]),
e = parseFloat(!t ? 0 : t[5]),
f = parseFloat(!t ? 0 : t[6]);

return $M([
[a, c, e],
[b, d, f],
[0, 0, 1]
]);
}



Now let's do some real work with those functions. Let's say we have a div called "myDiv" and we want to iteratively rotate it by 25 degrees. First, we need to get the current transform and convert it to a matrix:


var t = _T.fromString($('#myDiv').css('transform'));


Next, we need to build our rotation matrix:


var r = _T.rotate(25);


Now, we can multiply the existing transform matrix by the rotation matrix to find the combined transform:


var n = t.x(r);


Finally, we convert the new transform matrix back to a string and set the style on the element:


$('#myDiv').css({
transform: _T.toString(n)
});


That's all there is to it. Any existing transforms are preserved and the 25 degree rotation is added to whatever is already there without us having to know what it is. There's a complete demo of the different transform functions on my sandbox.

When you try the demo, you'll notice I made a note about "gimble lock". This can occur when applying successive rotations on a transform that already contains a rotation. This effect happens when working with Euler angles and is generally fixed by using quaternions to manage the rotation calculations. This is math I did not want to dive into right now. That means I will probably need to use data() to record the current angle and use that to build a new transform. I still think using matrices to calculate the transforms is the correct approach. The simplicity in the representation is very appealing. Additionally, since this is the approach used throughout the rest of the transformation world, it will be a lot easier to find examples and understand concepts if we're using the same method.

Friday, September 7, 2012

Tinkering with jQuery UI (Part 3): Overriding Default Behaviors with Proxy Functions

In part 2 of this series, we built a custom jQuery UI widget. At this point, our widget creates a box, attaches Draggable and Resizable to the element, provides a method to animate the colors on the box, and augments the drop event to track how far the box was moved. We are now faced with the problem that if Draggable and/or Resizable are called outside out the widget, the settings we made when building our widget (the 50x50 grid, etc) will be overridden. To find a way to avoid this, we have to understand how the internals of the widget object works. The key part is in $.widget.bridge(). This function is the plugin factory that extends $.fn and ensures only once instance of a given widget is created on an element. It also brokers all the method calls to our object instance. By following along in this code, we can see that if an instance is already attached to an element, the options are set and then _init() is called. Tracking the options call we can see that _setOption() is a common intersection where all calls are made to change options on a widget instance. If we could override this behavior for Draggable and Resizable, we could inject the options we want so they can not be overridden for ColorBox. We can do this with proxy pattern to alter the behavior of the function while maintaining the original functionality in appropriate cases:


var _draggable_setoption_orig = $.ui.draggable.prototype._setOption;
$.ui.draggable.prototype._setOption = function(key, value)
{
var _value = value;

if (this.element.data('colorbox'))
_value = _filter('draggable', key, _value);

_draggable_setoption_orig.call(this, key, _value);
}


Here, I'm leveraging the fact that the UI factory records the widget instance in the element's data container hashed by the widget name. If ColorBox is instantiated, then we want to inspect and possibly change the options being set. The _filter() function compares the set of options we want to override and will ensure that those values are set before calling the original _setOption function. Now, if someone tries to change options for Draggable outside of our widget, we'll catch it and fix it before it can be changed.

The only other issue we need to address are the Draggable events. We've augmented the Draggable stop callback to create a new event that calculates the distance the element moved. Any of the jQuery event listeners can be used to catch "dragstop" events on a Draggable. If we don't want that to happen on our Draggable instance, we will need to attach a listener to Draggable and call stopImmediatePropagation() to prevent any other listeners from receiving the event:



...
_create: function()
{
this.element
.addClass('colorbox')
.css(
{
height: '100px',
width: '100px',
backgroundColor: this.options.color
})
.resizable(_conf.resizable)
.draggable(_conf.draggable)
.on('dragstop', $.proxy(function(e) {alert(this.widgetName + ' drag done'); e.stopImmediatePropagation()}, this));
}

...


Here we attached to the "dragstop" event instead of using the callback approach in the original example. Now we can use stopImmediatePropagation() to prevent other handlers from executing. You can see a full example with source in my sandbox.

I prepared this example and it does works as expected. However, it made me wonder if adding the Draggable and Resizable inside ColorBox was an appropriate design decision. As I pondered that, I decided to see what happens to other jQuery UI widgets when overlapping interactions are attached to the same element. For example, consider the Sortable widget. It adds dragging features to a group of child elements and manages the movement of those children so you can rearrange them. If, for some reason, I also add Draggable to those children, what would happen to the Sortable widget? I created a demonstration to show the outcome. As you can see, the Draggable widget will override anything the Sortable had enabled. Considering this behavior, I decided that I needed to consider some guidelines for how I might build a widget.


  • Use the jQuery UI to "mix" non-overlapping interactions, behaviors, and layout enhancements on a target element. In this example, we added Draggable and Resizable to a element inside ColorBox. However, I believe ColorBox should only provide layout to the element - the decision to add dragging and resizing to the element should left up to the developer using ColorBox. Building small building blocks like this allows for better control and reuse. We could still have the proxies in ColorBox so that if Drabbable and Resizable are added to the element, we can customize their behavior.

  • Using other UI widgets inside another UI widget is fine if they are used on children of the target element. If the widget is creating dynamic content in the target element or enhancing existing markup, it makes sense to use those UI widgets. In this situation, the new UI widget is essentially controlling/coordinating the behavior of multiple UI widgets.
  • Use caution when adding UI widgets to elements. If there is overlapping functionality, odd behaviors may occur that can be difficult to trace. I don't think the test case I did for the Sortable and Draggable example is a problem - its just something to be aware of and code accordingly. Generally, you will not do what I did on purpose, however, as pages become more complex and dynamic, many UI widgets may get instantiated. Ensuring they are all attached to the correct elements is important to avoid hours of debugging hassle.



So that's the basics of building jQuery UI widgets. They provide many possibilities for building powerful UI components on top of jQuery's existing foundations. There are some definite design consideration to be aware of while constructing your widgets. But overall, the framework is a great toolset for building stateful and dynamic extension to jQuery.

Thursday, September 6, 2012

Rotating DOM Elements with CSS and jQuery

I've been doing some research for a project I'm working on that requires an option to rotate elements in various ways. I already knew that rotation in a browser is fairly new and has various levels of support in each browser. I was also aware of the CSS3 transform style.

So I started by just looking at the CSS aspect to see what was possible knowing that I could plug whatever I found into jQuery's css method.

At samuli.hakoniemi.net I found this basic demonstration of the different browser specific methods:


-moz-transform:rotate(45deg);
-webkit-transform:rotate(45deg);
-o-transform:rotate(45deg);
-ms-transform:rotate(45deg);


You can see the demo here.

At www.the-art-of-web.com we can go a little further and even apply animations:


#submenu {
background-color: #eee;
-webkit-transition: all 1s ease-in-out;
-moz-transition: all 1s ease-in-out;
-o-transition: all 1s ease-in-out;
-ms-transition: all 1s ease-in-out;
transition: all 1s ease-in-out;
}

#submenu:hover {
background-color: #fc3;
-webkit-transform: rotate(360deg) scale(2);
-moz-transform: rotate(360deg) scale(2);
-o-transform: rotate(360deg) scale(2);
-ms-transform: rotate(360deg) scale(2);
transform: rotate(360deg) scale(2);
}


I'm using the latest Firefox release and all the examples worked as expected. This is pretty exciting that you can actually apply complex motion only with CSS.

However, I know I will still need some additional control and will not just want a predefined transition. Since I am using jQuery, I wanted to see what was available to achieve dynamic rotational transforms and animations.

I thought jQuery might have something in place to do this already, but you pretty much have to coordinate animate() and css() together through the step callback:


$('#myDiv').animate({ left: 50, top: 50 }, {
step: function(now, fx) {
$(this).css('transform','rotate('+(fx.pos * 90)+'deg)');
},
duration: 2000,
easing: 'easeInQuint'
});


Try out the jsFiddle to see it work.

The nice thing jQuery does here, is normalize the CSS3 transform style and translate it into the browser specific notation. So you don't have to add all the possible variations in each time you use it. However, the animate() method does not natively support animating the transform style so you have to use the step callback to achieve that part of the animation.

The above approach seems doable but I was wondering what other choices might exist to make it easier to animate transforms like rotations. So I did a quick search and found three possible alternatives:



  • zachstronaut.com - A jQuery patch to add rotation and scale plugins and animations. It exclusively uses the CSS3 transform attribute so will only be functional in relatively new browsers. Very little code to make it happen so there's no giant footprint added to get this functionality.



  • jquery.transit - An alternative to jQuery css and animate. You use the transition plugin to do all your animation. The augmented css() method provides more expressive ways to apply transforms. The animate method understands how to animate tranform styles. This seemed a little more than I needed but appears to be a fairly powerful plugin.


  • jqueryrotate - This supports older browsers but can only rotate images.



With this knowledge, I think some prototyping is needed to see if any of these libraries will benefit what I'm building.

Wednesday, September 5, 2012

Joose: Object-Oriented Programming with Javascript Made Easy

Last week I posted about a site that had a nice list of software coding patterns with examples in Javascript. While researching some patterns, I found Joose


Joose is a self-hosting meta object system for JavaScript with support for classes, inheritance, mixins, traits, method modifiers and more.

Joose makes object-oriented programming with JavaScript easy, declarative and very productive. The Joose meta-object system is multi-paradigm. It supports class-based and prototype-based programming styles as well as class-based inheritance and role-based extention.


What's interesting about Joose, is its not a DOM access, traversal, enhancement library. Its simply a framework to enable easy application of specific OO patterns in Javascript. If you're a Perl 5 developer, you might have come across Moose. Joose is heavily inspired by that framework but is for Javascript.

I haven't had a chance to tinker with it yet - I just dug through the docs and examples a bit to see what its all about. Its something I might revisit and see if I can find a use for in some of my jQuery projects.

Tuesday, September 4, 2012

Tinkering with jQuery UI Widgets (Part 2): Creating a Custom Widget

In part 1 of this series, we explored chaining several jQuery UI widgets together to enhance our element to do interesting things with the built-in behaviors defined in the UI library. Now we want to create our own widget to encapsulate some new behaviors that utilize the existing UI components and add some new ones specific to our widget.

The pattern for creating jQuery UI widgets is pretty straight forward. Here is the template with some explanations of each section:


(function ($)
{
$.widget("namespace.widgetname",
{
options:
{
// name: value pairs that will
// be set when you call widgetname({options...})
// on an element. Set defaults here
},

_create: function()
{
// Called first time widgetname() is
// called on an element.
// Put one time setup code here that
// are not affected by config options.
// Adding a class to the element is a
// common practice to show that this
// widget has been attached to it.
},

_init: function()
{
// Called everytime widgetname() is
// called on an element. Called after _create
// on initial call on an element
// Put reinitialization code here since
// config options may have been changed
},

_setOption: function(option, value)
{
// Called when widgetname('option' (or 'options'), 'name', value)
// is called. Use switch block to handle each option you've defined
// in the options object.

// Chain to super class (changes in 1.9)
$.Widget.prototype._setOption.apply( this, arguments );
},

destroy: function()
{
// Clean up references you have stored so
// garbage collection can happen. Events are the
// biggest issue. However, good practice is to
// return the element back to its original state
// before this widget enhanced it.

// Chain to super class (changes in 1.9)
$.Widget.prototype.destroy.call( this );
}

});
}
)(jQuery);


Extending the example from the previous post, we'll make a ColorBox widget to contain the functionality we built in that post:


(function ($)
{
// Make your own namespace - do not use 'ui'
$.widget("sdt.colorbox",
{
options:
{
// We're going to have one option for now
// to set the color of the box and set the
// default to 'green'
color: 'green'
},

_create: function()
{
// this.options is now set so we can
// use it to customize our box.
// this.element is a jQuery object
// representing the target element(s)
// 'this' is the widget object not
// a DOM node or jQuery object.
// all of our widget state and data
// can be stored in this so we have
// it throughout the lifecycle of this
// widget.

this.element
.addClass('colorbox')
.css(
{
height: '100px',
width: '100px',
border: '2px solid black'
})
.resizable(
{
aspectRatio: true,
maxHeight: 500,
maxWidth: 500,
minHeight: 100,
minWidth: 100
})
.draggable(
{
grid: [50, 50],
stop: function (e, ui)
{
alert('done dragging!');
}

});
},

_init: function()
{
// Each time colorbox() is called on an element,
// _init is called. On the first colorbox() call,
// _create is called, then _init. One time setup
// tasks that are not affected by the options,
// can go into _create. Since each call
// to colorbox() can change the options, we
// need to handle that here:

this.element
.css(
{
backgroundColor: this.options.color
});
},

_setOption: function(option, value)
{
// If colorbox('option', 'color', 'newcolor')
// is called, its that same as just reinitializing
// the widget. Need to call the default behavior first
// if we're not doing anything special and
// then call init

$.Widget.prototype._setOption.apply( this, arguments );

this._init();
},

destroy: function()
{
// Need to reset this element(s) back to
// what they were before we enhanced them.
// this includes destorying the other UI
// widgets we created.

this.element
.removeClass('colorbox')
.css(
{
height: '',
width: '',
backgroundColor: ''
})
.resizable('destroy')
.draggable('destroy');

$.Widget.prototype.destroy.call( this );
}

});

}
)(jQuery);


Now we can use this widget like any other UI widget:


<div class="boxes">
<div></div>
<div></div>
<div></div>
</div>

<div id="onebox">




$(function()
{
// Collection of boxes
// will use default color: 'green'
$('.boxes > div').colorbox();

// One box - will use color: 'red'
$('#onebox').colorbox({color: 'red'});

});


With minimal code, our widget is doing some pretty amazing things. However, there are some missing pieces we have not discussed. First, why do some of the function start with an underscore (_) and others do not? The answer: functions with underscores are internal or "private" to the widget and not callable by the outside world. Functions without underscores are considered "public". Our widget has 3 "private" functions and 1 "public" function. These are part of the abstract widget class that we are extending and overriding. A public function is called by calling the widget name on an element with the function name as the first parameter and all its arguments as the remaining parameters. So let's say we want to create a way for our box to rotate through several colors. We could create a function animate() in our widget class and then call that method via the colorbox() plugin function:



// Define animate()
(function ($)
{
// Make your own namespace - do not use 'ui'
$.widget("sdt.colorbox",
{
... other widget code
animate: function (colors, speed)
{
// trigger necessary effects
// here
}
... more widget code
}
})(JQuery)

// Call it on our widget
$('mybox').colorbox('animate', ['blue', 'green', 'red'], 100);



The widget framework routes the call to our animate function and passes the last two arguments into colors and speed respectively. Why do this instead of just calling animate directly? Because the UI framework does all the necessary work to return a jQuery object so further chaining can be done. We don't have to do anything. If we do want to return a specific value, we could add a return to the function and the UI framework will avoid the default behavior of returning a jQuery object and, instead, return the value we sent back in our return statement.

The second piece of functionality we need is how to trigger our own events that the outside world can listen to? The UI framework provides a special _trigger function that provides some special behavior not present in the normal jQuery.trigger method. The most notable is defining both a callback function and an event that can be bound using the standard jQuery event methods.

You may have noticed in our widget definition we used the callback "stop" to attach to the draggable's stop event:


.draggable(
{
grid: [50, 50],
stop: function (e, ui)
{
alert('done dragging!');
}

});


We could have also bound to this event like this:


.draggable(
{
grid: [50, 50],
}).bind('dragstop', function (e, ui)
{
alert('done dragging!');
});



Either method is perfectly fine and depends on how you want to organize your code. Now, how to we make our own custom event like draggable's stop? Simple, call _trigger() with the name of our custom event. An event object is automatically created or we can pass one to _trigger in the second argument. Additionally, we can pass along spacial information in the third arguement. Draggable does the same thing - you get a ui object in your handler with special information related to the draggable object. So we could add a "moved" event which is triggered from draggable's stop event. Our widget will add some special information to the data that might be useful to outside handlers:



// Define animate()
(function ($)
{
// Make your own namespace - do not use 'ui'
$.widget("sdt.colorbox",
{
var self = this;

... other widget code
.draggable(
{
grid: [50, 50],
stop: function (e, ui)
{
self._trigger('moved', e, {special: 'my data'});
})
});

... more widget code
}
})(JQuery)

// Handle it on our widget:
$('mybox').colorbox(
{
moved: function (e, info)
{
alert('I moved: ' + info.special);
}
});



Now we have our event tied in - notice how it just passes along the event from draggable's stop and then adds a simple data object to pass to the handler function.

This is the basic process of building a new widget within the jQuery UI library. The pattern is simple and easy to extend so you can make powerful, reusable widgets. A full demo of the ColorBox widget can be found here. The demo includes an actual implementation of the animate function and the "moved" event.

Now that we have a basic widget there is a problem that needs to be addressed. Nothing stops someone from chaining draggable or resizable after calling colorbox to change the behavior you just added inside colorbox. In part 3 of this series, we'll investigate this problem by looking some alternative patterns and solutions.