Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery-UI sortable: Animate from original position to new after helper drop

TLDR

I want to be able to animate my jQuery-UI sortable after the helper has been dropped by showing the original item moving to it's new position with the others shifting around. This is complicated by certain sortables needing to be fixed in position.

The Project:

The project involves dynamically generated jQuery-UI portlets within a tabbed interface. The tabs and portlet contents themselves are generated through async AJAX requests to established web services. Each tab has an undetermined number of portlets within it, and uses custom scrollbars for content display. I have implemented the fixing of certain portlets using classes and a solution found elsewhere on stack overflow (slightly modified). A very basic implementation of current functionality can be seen here: http://jsfiddle.net/99yVq/.

The Problem

Prior to implementing portlet fixing I was animating while sorting by showing and hiding the placeholder element:

$( ".content" ).sortable({       
    start: function(e, ui){
        $(ui.placeholder).hide(300);
    },
    change: function (e,ui){
        $(ui.placeholder).hide().show(300);
    }
});

Viewable in action here: http://jsfiddle.net/BWNE2/. This works fairly well but I feel the animation is choppy and it will obviously require some poking to get it to work with the portlet fixing solution above (as it relies on helper creation to determine fixed positions). It is not the solution I'm looking for.

Update

Did the poking to combine the two (none was really needed with a caveat) and the combined functionality is available here: http://jsfiddle.net/BWNE2/1/. The animations work but as you can see the .fixed portlet animates along with the others and then reverts back to it's original position.

End of Update

The Question

My ideal scenario would be for the user to drag the portlet helper (which I believe is a clone of the original item used during dragging) from its original position to it's new one. At the point of dropping the portlets would then animate from there old positions to the new. The current movement during sorting should be maintained to give the end user feedback, but the final animations should take place after the drop of the helper.

Does anyone have any ideas or examples of how to achieve this?

If my question format is inadequate or you require more information on anything please let me know.

Thanks in advance.

Edit

You can see here: http://jsfiddle.net/BWNE2/2/ that the animation effect is even more inadequate when the fixed portlet it in position 3 (top right of the top section). It is often not in the correct position while sorting, though it does revert to the correct place on sort end.

like image 684
ChrisSwires Avatar asked Jan 31 '14 10:01

ChrisSwires


1 Answers

Following the solution found in other question, which is basically when dragging you clone the itens and animate them to the new positions, I merged that into your code.

What this code didn't had was the animation after the release, which i made by saving the position on the mouseup event, and animating the dragged element to the final position.

Final fiddle: http://jsfiddle.net/hTgad/

Code:

var lastPosition;

$( "#content" ).sortable({
    delay: 100,
    distance: 10,
    handle: '.portlet-header',
    items: '.portlet:not(.fixed)',
    start: function(e, ui)
    {
        //store the fixed itens position
        $('.fixed', this).each(function(){
            var $this = $(this);
            $this.data('pos', $this.index());
        });

        // Identify the item being dragged
        ui.helper.addClass("being-dragged");

        var clonedItems = $("#cloned_items");

        // Create a clone for every item, except the fixed ones and the one being dragged            
        $("#content .portlet:not(.being-dragged, .ui-sortable-placeholder, .fixed)").each(function(){
            // clone the original items to make their
            // absolute-positioned counterparts...
            var original = $(this);
            var clone = original.clone();
            // 'store' the clone for later use...
            original.data("clone", clone);
            original.css("visibility", "hidden"); // Hide the original

            // set the initial position of the clone
            var position = original.position();
            clone.css("left", position.left)
                 .css("top", position.top);

            // append the clone...
            clonedItems.append(clone);
        });
    },
    change: function(e,ui) 
    {
        //change the position of the fixed elements to the original one
        $sortable = $(this);
        $statics = $('.fixed', this).detach();
        $helper = $('<div class="portlet" style="background-color:#000"></div>').prependTo(this);
        $statics.each(function(){
            var $this = $(this);
            var target = $this.data('pos');
            $this.insertAfter($('.portlet', $sortable).eq(target));
        });
        $helper.remove();

        // animate all clones to the new position
        $("#content .portlet:not(.being-dragged, .ui-sortable-placeholder, .fixed)").each(function(){
            var item = $(this);
            var clone = item.data("clone");

            // stop current clone animations...
            clone.stop(true, false);

            var position = item.position(); // New position
            clone.animate({left: position.left, top:position.top}, 500);
        });

    },

    stop: function(e, ui){
        var el = $("#content .being-dragged");
        // Save the new position
        var newPosition = el.position();
        // Insert a placeholder for the final animation
        $('<div class="portlet ui-sortable-placeholder"></div>').insertBefore(el);
        el.css("left", lastPosition.left)
          .css("top", lastPosition.top)
          .css("position", "absolute")
          .animate({left: newPosition.left, top: newPosition.top}, 300, "swing", function() {
              // After the animation remove the placeholder and reset the element position
              $("#content .ui-sortable-placeholder").remove();
              $(this).css("left", "").css("top", "").css("position", "");
          })
          .removeClass("being-dragged");

        // Erase the temporary itens
        $("#cloned_items").empty();

        // make sure all our original items are visible again...
        $("#content .portlet").css("visibility", "visible");
    }

});

// Save the position of the element being dragged for the final animation
$(".portlet").on("mouseup", function() {
    lastPosition = $(this).position();
});
like image 102
ariel Avatar answered Sep 29 '22 07:09

ariel