A bit late to the party, but I was determined to get a solution going with jQuery as there was very little help on this topic, especially replicating the functionality that exists on web apps like Facebook and their albums' images dragging and dropping to reorder, and the pretty animations that go along with that...
So I've come up with a solution that seems to work pretty great, and I'll do my best to explain it to the best of my abilities! Here goes...
The biggest problem here was to not only animate the sortables, but to figure out where they needed to animate to - fantastic when it comes to floating elements like images in a gallery! To get around this, I decided to .clone()
the original floating LI
items, position the clones absolutely under the original LI
items using a z-index
value that was less than the original LI
items, and then when the change
event fired from the jQuery sortable I could detect the position of the original LI
and animate the absolutely positioned clones to those positions. The rest was to simply show / hide elements appropriately to get the desired effect.
Here's the code, starting with the HTML:
<ul id="original_items">
<li><img src="something.jpg" /></li>
<li><img src="something.jpg" /></li>
<li><img src="something.jpg" /></li>
</ul>
<ul id="cloned_items">
</ul>
So we have the original items we're trying to sort, and a container for the cloned items. Time for the CSS:
#original_items, #cloned_items {
list-style: none;
}
#original_items li {
float: left;
position: relative;
z-index: 5;
}
#cloned_items li {
position: absolute;
z-index: 1;
}
With our CSS, we're just removing any list styling, floating our original elements, and setting up the z-index
requirements to ensure the cloned items lie underneath the original items. Note the relative
position on the original items to make sure they behave as expected. Why underneath you ask? It will (hopefully) become clear with some Javascript:
jQuery(function(){
// loop through the original items...
jQuery("#original_items li").each(function(){
// clone the original items to make their
// absolute-positioned counterparts...
var item = jQuery(this);
var item_clone = item.clone();
// 'store' the clone for later use...
item.data("clone", item_clone);
// set the initial position of the clone
var position = item.position();
item_clone.css("left", position.left);
item_clone.css("top", position.top);
// append the clone...
jQuery("#cloned_items").append(item_clone);
});
// create our sortable as usual...
// with some event handler extras...
jQuery("#original_items").sortable({
// on sorting start, hide the original items...
// only adjust the visibility, we still need
// their float positions..!
start: function(e, ui){
// loop through the items, except the one we're
// currently dragging, and hide it...
ui.helper.addClass("exclude-me");
jQuery("#original_items li:not(.exclude-me)")
.css("visibility", "hidden");
// get the clone that's under it and hide it...
ui.helper.data("clone").hide();
},
stop: function(e, ui){
// get the item we were just dragging, and
// its clone, and adjust accordingly...
jQuery("#original_items li.exclude-me").each(function(){
var item = jQuery(this);
var clone = item.data("clone");
var position = item.position();
// move the clone under the item we've just dropped...
clone.css("left", position.left);
clone.css("top", position.top);
clone.show();
// remove unnecessary class...
item.removeClass("exclude-me");
});
// make sure all our original items are visible again...
jQuery("#original_items li").css("visibility", "visible");
},
// here's where the magic happens...
change: function(e, ui){
// get all invisible items that are also not placeholders
// and process them when ordering changes...
jQuery("#original_items li:not(.exclude-me, .ui-sortable-placeholder)").each(function(){
var item = jQuery(this);
var clone = item.data("clone");
// stop current clone animations...
clone.stop(true, false);
// get the invisible item, which has snapped to a new
// location, get its position, and animate the visible
// clone to it...
var position = item.position();
clone.animate({
left: position.left,
top:position.top}, 500);
});
}
});
});
Wow, I really hope this makes sense and helps someone animate their sortable lists, but this is a working example for anyone who's interested! :)