Search code examples
jquerylistjquery-animatetransition

Animate multiple lists collapsing into one


I have multiple lists on a page. The number of lists that could exist is arbitrary, but would likely be limited to 10.

There are two views of this page:

  • Mode 1: All items are categorized and listed in separate lanes.
  • Mode 2: All items are collapsed into a single list.

A user should be able to toggle between the two modes. I have this [roughly] working - they are collapsed into a single list, but they lose their styling from their parent/origin list.

Ideally, I'd love the transition between the two modes to be animated; that is, when the user clicks the mode toggle button, all of the list items from each lane animate/collapse into a single, alphabetized list. Once in Mode 2 (single list mode), clicking the toggle again would, ideally, animate the list items back into their original lists.

Here's a Fiddle I've been working on.

In my code, I have four lists, with a hidden <ul> that the items can collapse into. I'm having trouble figuring out how and where the animation goes.

$(function() {
    $("button").on("click", function() {
        if($(this).text() == "Mode 1") {
            $("#list").show();
            $("li").remove().clone().appendTo("#list");
            $("ul:not(#list)").hide();
            $("button").text("Mode 2");
        }
    });
});

<button>Mode 1</button>

// The hidden list
<ul id="list"></ul>

<ul id="red">
    <li>A</li>
    ...
</ul>
<ul id="blue">
    <li>K</li>
    ...
</ul>
<ul id="gray">
    <li>U</li>
    ...
</ul>
<ul id="green">
    <li>AA</li>
    ...
</ul>

EDIT: I guess I should specify the animation I'm aiming for. I'd like each individual list item to "fly" from its origin position into the single, collapsed list.


Solution

  • Alright Jon, for Each piece to animate from it's current position to a new position, you have get each element's offset, make them position:absolute; then animate them to the correct place.

    In-progress fiddle here

    Here's the jQuery to accompany it. this also keeps your HTML layout the same:

    $(function() {
    $("button").on("click", function() {
        if($(this).text() == "Mode 1") {
            var elem = $('ul:not(#list) > li'),
                elemH = elem.first().outerHeight(),
                list1Last = {x:$('#red > li:last-of-type').offset().left,
                             y:$('#red > li:last-of-type').offset().top},
                oElem = {x:0, y:0, w:0, h:0}
            $('ul:not(#red)').each(function(e){
                $(this).find('li').each(function(i){
                    oElem = {x:$(this).offset().left,
                             y:$(this).offset().top,
                             w:$('#red').width(),
                             h:$(this).outerHeight()}
                    console.log(oElem.x, oElem.y);
                    $(this).css({"position":"absolute", 
                                 "top":oElem.y+(oElem.h*i)+"px",
                                 "left":oElem.x+"px",
                                 "width":oElem.w+"px"});
                    $(this).delay(200*i).animate({"top":(list1Last.y+elemH)*e+(i*elemH)+"px",
                                                  "left":list1Last.x+"px"}, 200);
                });
            })
        } else {
    
        }
    });
    });
    

    What this does is it gets each elements current position using offset(), then I change the CSS to make them position:absolute; and give them a left and top value. This seamlessly makes them absolutely positioned without any bumping. Then right away, I animate them to their new spot, almost like they're flying as you say. There's ways to get them to fly one by one, but that will require a bit more editing.