web-dev-qa-db-fra.com

JQuery - animer un élément DOM en mouvement vers un nouveau parent?

J'ai une balise d'image à l'intérieur d'une cellule de tableau, que j'aimerais bien déplacer dans une autre cellule de tableau, et animer ce mouvement.

Le code ressemble à quelque chose comme ça ...

<td id="cell1"><img src="arrow.png" alt="Arrow"/></td>
<td id="cell2"></td>

J'aimerais passer de "arrow.png" à "cell2" et avoir une sorte d'effet de transition, de préférence avec JQuery.

Des idées?

Merci!

30
Lee

C'est en fait assez difficile car vous devez le supprimer et l'ajouter au DOM tout en conservant sa position. Je pense que vous cherchez quelque chose comme ça. Fondamentalement, nous n'animons ni la flèche dans #cell1 ni #cell2. Nous venons de créer un nouveau marqueur dans la variable body- et de l'animer. De cette façon, nous n'avons pas à nous préoccuper de la position des cellules du tableau car nous pouvons nous positionner par rapport au document.

var $old = $('#cell1 img');
//First we copy the arrow to the new table cell and get the offset to the document
var $new = $old.clone().appendTo('#cell2');
var newOffset = $new.offset();
//Get the old position relative to document
var oldOffset = $old.offset();
//we also clone old to the document for the animation
var $temp = $old.clone().appendTo('body');
//hide new and old and move $temp to position
//also big z-index, make sure to edit this to something that works with the page
$temp
  .css('position', 'absolute')
  .css('left', oldOffset.left)
  .css('top', oldOffset.top)
  .css('zIndex', 1000);
$new.hide();
$old.hide();
//animate the $temp to the position of the new img
$temp.animate( {'top': newOffset.top, 'left':newOffset.left}, 'slow', function(){
   //callback function, we remove $old and $temp and show $new
   $new.show();
   $old.remove();
   $temp.remove();
});

Je pense que cela devrait vous orienter dans la bonne direction.

51
Pim Jager

La réponse de @Pim Jager est plutôt bonne. Toutefois, si vous avez des références d'objet à l'élément d'origine, elles se briseraient car l'élément d'origine a été remplacé par un clone.

J'ai proposé ce que je pense être une solution légèrement plus propre, dans la mesure où il n'y a qu'un seul clone qui apparaît pour l'animation puis s'en va, laissant l'original dans le nouvel emplacement.

function moveAnimate(element, newParent){
    //Allow passing in either a jQuery object or selector
    element = $(element);
    newParent= $(newParent);

    var oldOffset = element.offset();
    element.appendTo(newParent);
    var newOffset = element.offset();

    var temp = element.clone().appendTo('body');
    temp.css({
        'position': 'absolute',
        'left': oldOffset.left,
        'top': oldOffset.top,
        'z-index': 1000
    });
    element.hide();
    temp.animate({'top': newOffset.top, 'left': newOffset.left}, 'slow', function(){
       element.show();
       temp.remove();
    });
}

Pour utiliser: moveAnimate('#ElementToMove', '#newContainer')

50
Davy8

Vous devrez le faire en deux étapes: (1) animation (2) restitution.

L'animation dont vous pouvez vous occuper avec .animate (), comme le souligne @Ballsacian. Le rapatriement peut être accompli avec .html () - pour l'exemple ci-dessus,

var arrowMarkup = $('#cell1').html(); //grab the arrow
$('#cell1').html(""); //delete it from the first cell
$('#cell2').html(arrowMarkup); //add it to the second cell

Bien entendu, vous devrez compliquer ce code pour intégrer l'animation. Et cette façon de faire ne va pas forcer la sélection (je suppose que vous sélectionnez une ligne de tableau?) À activer les lignes entre l’ancienne sélection et la nouvelle, lorsque la flèche les dépasse. Ce serait encore plus complexe à réaliser.

3

J'ai étendu une des autres réponses un peu plus loin afin que vous puissiez maintenant passer un objet en tant que troisième paramètre qui sert de véhicule lors de l'animation. Par exemple, si vous souhaitez déplacer certains <li> d'un <ul> à un autre, votre <ul> a probablement une classe qui donne à <li> son style. Il serait donc très utile d’animer votre <li> dans un véhicule temporaire <ul> qui offre le même style que le source ou la cible <ul> de l’animation:

//APPENDS AN ELEMENT IN AN ANIMATED FASHION
function animateAppendTo(el, where, float){
    var pos0 = el.offset();
    el.appendTo(where);
    var pos1 = el.offset();
    el.clone().appendTo(float ? float : 'body');
    float.css({
        'position': 'absolute',
        'left': pos0.left,
        'top': pos0.top,
        'zIndex': 1000
    });
    el.hide();
    float.animate(
        {'top': pos1.top,'left': pos1.left},
        'slow',
        function(){
           el.show();
           float.remove();
        });
}
2
tobibeer

J'essayais la fonction de @ Davy8, ce qui est assez bon, mais je l'ai trouvée assez choquante lorsque l'élément déplacé a été retiré de la page au début, puis à la fin. Les autres éléments de page se déplaçant soudainement ont interrompu une animation sinon fluide, mais cela dépend probablement de la mise en page.

Il s’agit donc d’une version modifiée de la fonction de @ Davy8, qui devrait également se contracter en douceur et augmenter l’espace entre les parents.

function moveAnimate(element, newParent,
                     slideAnimationSpeed/*=800*/, spacerAnimationSpeed/*=600*/)
{
    //Allow passing in either a jQuery object or selector
    element = $(element);
    newParent= $(newParent);
    slideAnimationSpeed=slideAnimationSpeed||800;
    spacerAnimationSpeed=spacerAnimationSpeed||600;

    var oldOffset = element.offset();
    var tempOutgoing=element.clone().insertAfter(element);
    tempOutgoing.hide(); //Don't take up space yet so 'newOffset' can be calculated correctly
    element.appendTo(newParent);
    var newOffset = element.offset();

    var tempMover = element.clone().appendTo('body');
    tempMover.css({
        'position': 'absolute',
        'left': oldOffset.left,
        'top': oldOffset.top,
        'z-index': 1000,
        'margin':0 //Necessary for animation alignment if the source element had margin
    });

    element.hide();
    element.show(spacerAnimationSpeed).css('visibility', 'hidden'); //Smoothly grow space at the target

    tempMover.animate({'top': newOffset.top, 'left': newOffset.left}, slideAnimationSpeed, function(){
       element.css('visibility', 'visible');
       tempMover.remove();
    });
    tempOutgoing.show().css('visibility', 'hidden');
    tempOutgoing.hide(spacerAnimationSpeed, function(){ tempOutgoing.remove() }); //smoothly shrink space at the source
}
1
LukePH

Si l'animation ne doit pas forcément être en mouvement, cette question, qui utilise fadeIn et fadeOut, donne une réponse simple et propre, sans clonage, et transmet toujours très bien le mouvement:

Ré-ordonner les positions div avec jQuery?

0

Pour tous ceux qui visionnaient encore cela, j’ai trouvé que les exemples fournis ne correspondaient pas exactement à ce que je voulais et qu’ils ne tenaient pas compte des marges. Voici donc ma version:

jQuery.fn.extend({
    moveElement : function (newParent, speed, after) {
        var origEl   = $(this);
        var moveToEl = $(newParent);

        var oldOffset = origEl.offset();
        var temp      = origEl.clone().appendTo('body');

        temp.css({
            'position' : 'absolute',
            'left'     : parseInt(oldOffset.left) - parseInt(origEl.css('margin-left')),
            'margin'   : origEl.css('margin'),
            'top'      : oldOffset.top,
            'z-index'  : 1000,
            'height'   : moveToEl.innerHeight(),
            'width'    : moveToEl.innerWidth()
        });

        var blankEl = $('<div></div>').css({
            height   : moveToEl.innerHeight(),
            margin   : moveToEl.css('margin'),
            position : 'relative',
            width    : moveToEl.innerWidth()
        });

        if (after) {
            origEl.insertAfter(moveToEl);
            blankEl.insertAfter(newParent);
        }
        else {
            origEl.insertBefore(moveToEl);
            blankEl.insertBefore(newParent);
        }
        origEl.hide();

        var newOffset = blankEl.offset();

        temp.animate({
            'top'  : blankEl.offset().top - parseInt(moveToEl.css('margin-top')),
            'left' : newOffset.left - parseInt(moveToEl.css('margin-left'))
        }, speed, function () {
            blankEl.remove();
            origEl.show();
            temp.remove();
        });
    }
});

Déplacer un élément avant un autre: $('.elementToFind').moveElement('.targetElement', 1000);

Déplacer un élément après un autre: $('.elementToFind').moveElement('.targetElement', 1000, 'after');

0
Nathan