CSS3 transforms vs jQuery Draggable

Jan
2013
24

HTML5, Programming

No comments

Every now and then I’m forced to exit the Candyland Paradise known as Python and I have to lower myself into the muddy waters of Javascript…the things I do for money.

Anyway, while developing an HTML5 app for 2wav, which makes heavy use of CSS3 transforms via the Barajas plugin (do check it out, it’s great), I found that trying to drag a card using jQuery UI’s draggable component doesn’t quite work. There’s even a bug report about it, which sadly is marked as “won’t fix”, so you have to resort to workarounds to make it behave as expected.

The workaround looks like this:

    $(li).draggable({ 
                        revert: true,
                        start: function() {
                            /* Temporarily revert the transform so drag and dropping works as expected */
                            var parentRect = $(this).parent()[0].getBoundingClientRect();
                            var rect = this.getBoundingClientRect();
                            /* cssBrowserPrefix is one of: -moz -webkit -o */
                            $(this).css(cssBrowserPrefix + 'transition', 'all 0 ease 0');
                            $(this).css('transform', 'none');
                            $(this).css('left', rect['left']-parentRect['left']);
                          },
                        stop: function() {
                          /* Revert the transformation changes done on start, if needed */
                        }
                    });

What this does is reset the transition (so the change is done immediately) and the transform properties, and replaces the former for the equivalent value in the left property. I’m not doing rotation, scaling or Y axis translation here, solving the problem in those cases may be a little more involved but the principle is the same.

As a side note, in case you are interested in knowing about the underlying technical details, the reason why draggable has problems with CSS transforms is because these transforms operate in a kind of “one way” after all the other CSS properties have been applied to the element (which probably stems from the fact that they are usually implemented using 3D hardware accelerated operations; that is the rotation, scaling and translation is done directly on the GPU). The bottom line is that if you use a translateX transform, the left CSS property will remain unaffected, given the transformation matrix is applied onto the element after it’s been positioned on the page according to all the other HTML and CSS rules.

While this method is way faster than computing and doing the transformation on the CPU,  a common problem with this approach appears when eventually the user (and the browser itself, in case it needs to apply mouse interaction such as hovering) may need to know where the element ended up visually, for example, to drag it around. So, to get this final position you have to call  getBoundingClientRect as illustrated in the code snippet.