JQuery - 将 DOM 元素移动到新父元素的动画?

IT技术 javascript jquery dom jquery-ui animation
2021-03-19 19:42:23

我在表格单元格内有一个图像标签,我想移动到另一个表格单元格,并为该移动设置动画。

代码看起来像这样......

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

我想将“arrow.png”移动到“cell2”,并具有某种过渡效果,最好使用 JQuery。

有任何想法吗?

谢谢!

6个回答

这实际上非常困难,因为您必须将其删除并将其添加到 DOM 但保持其位置。我想你正在寻找这样的东西。基本上,我们没有动画无论是在箭头#cell1#cell2我们只是在body-tag 中创建一个新的并对其进行动画处理。这样我们就不必担心表格单元格的位置,因为我们可以相对于文档进行定位。

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();
});

我认为这应该为您指明正确的方向。

你有我的“很好的答案”徽章:)
2021-04-24 19:42:23
如果您有一个项目列表并且您还需要其他项目动画以腾出空间怎么办?
2021-05-08 19:42:23

@Pim Jager 的回答非常好,但是如果您有对原始元素的对象引用,它们会中断,因为原始元素已被克隆替换

我想出了一个我认为稍微更简洁的解决方案,因为它只有一个克隆,显示动画然后消失,将原件留在新位置。

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();
    });
}

使用: moveAnimate('#ElementToMove', '#newContainer')

很棒的功能。效果很好!
2021-04-29 19:42:23
我一直在该函数上收到“未捕获的类型错误:无法读取未定义的属性 'left'”错误,碰巧知道为什么?我按照你说的用了。
2021-05-06 19:42:23
Ya 现在可以正常工作,但如果我尝试在循环中使用它,动画似乎“损坏”了
2021-05-09 19:42:23
@eric.itzhak 是的,由于动画是异步发生的,因此您需要一种方法来将它们排入队列。最直接的方法是添加回调函数参数并设置递归调用,并以适当的结束方式退出。完全解释这有点超出本答案的范围,但是如果您搜索如何在 JS 中使用回调,您应该能够找出答案。否则,您可以提出一个新问题。
2021-05-10 19:42:23
我已经改进了你的代码片段,把它变成一个小的 jQuery 插件并稍微清理一下:gist.github.com/MadLittleMods/7257ee631210215e368e
2021-05-12 19:42:23

您需要分两步执行此操作:(1) 动画 (2) 重新定位。

正如@Ballsacian 指出的那样,您可以使用 .animate() 处理动画。可以使用 .html() 完成重新定位 - 对于上面的示例,

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

当然,您必须使该代码复杂化以集成动画。这样做的方式不会导致选择(我假设您正在选择表格行?)在箭头经过时激活旧选择和新选择之间的行。实现起来会更加复杂。

我已经进一步扩展了其他答案之一,以便现在您可以将对象作为第三个参数传递,该参数在动画期间用作车辆。例如,如果您想将一些 <li> 从一个 <ul> 移动到另一个,您的 <ul> 可能有一个特定的类,该类为 <li> 提供了样式。因此,在临时车辆 <ul> 内为您的 <li> 设置动画真的很方便,该车辆提供与动画的源或目标 <ul> 相同的样式:

//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();
        });
}

我正在尝试@Davy8 的功能,该功能非常好,但是当移动的元素在开始时从页面上折断然后在最后又折回时,我发现它很不和谐。其他页面元素突然移动中断了原本流畅的动画,但这可能取决于您的页面布局。

所以这是@Davy8 函数的修改版本,它也应该平滑地缩小和扩大父母之间的空间。

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
}