当其他元素消失时,动画/缓和元素的位置

Posted

技术标签:

【中文标题】当其他元素消失时,动画/缓和元素的位置【英文标题】:Animate/Ease an element to position when other elements disappear 【发布时间】:2012-10-06 06:54:54 【问题描述】:

请看一下这个小提琴:http://jsfiddle.net/dhcyA/

尝试点击一个块。我想要的是当其他元素消失时,选定的块将动画/缓和到他的给定位置,而不是像现在这样跳跃。然后,当再次单击该框时,相同的动画会自行重复,然后又回到原处。

也许要记住: 我使用的是响应式设计,这意味着这些块在缩放窗口后可以是垂直和水平的。

对小提琴的任何重新修改或建议都会很棒!

【问题讨论】:

您希望它在直线、列或正方形中的块时平滑滑动?还是全部? 我真的不会用浮动 div 来做,这是先决条件吗? 在这种情况下是的.. 你看我有一个响应式设计。 我要做的就是:jsfiddle.net/5FvtY/3 只需通过 JS 将您的浮点数转换为绝对值,然后再进行移动。解决不了多少,但也许可以让你前进一点。 谢谢您的输入,但是如果您在单击时将位置设置为绝对位置并在动画后返回相对位置呢? 【参考方案1】:

这是我的解决方案。

在您现有的标记上,我添加了一个包装器划分来计算包装器内框的位置。像这样

<div id="wrapper">
    <div class="block">
        <h2>I'm block 1</h2>
    </div>
    ....
</div>

为了保持块的流动性,我创建了一个函数来将块定位在包装器上。这是块位置的函数:

var reposition = function() 
    wrapper = $("#wrapper");
    console.log(wrapper.innerWidth());
    pLeft = 0;
    pTop = 0;
    maxRowHeight = 0;
    $(".block").each(function()
        if($(this).data('active')) 
            $(this).data('top', pTop);
            $(this).data('left', pLeft);
         else 
            $(this).stop(0,0).animate(
              'top' : pTop + 'px',
              'left' : pLeft + 'px'
            );
        
            pLeft += $(this).outerWidth() + parseInt($(this).css('marginLeft'));
            if($(this).height() > maxRowHeight) maxRowHeight = $(this).outerHeight() + parseInt($(this).css('marginTop')); //Find out the longest block on the row

            if(pLeft + $(this).next().outerWidth() + parseInt($(this).next().css('marginLeft')) >= wrapper.innerWidth()) 
               pLeft = 0;
               pTop += maxRowHeight;
               maxRowHeight = 0;
            

    );    
;

最后是切换块的脚本

$(".block").click(function() 

    $(this).siblings().slideToggle('slow'); //Toggle other blocks

    if(!$(this).data('active')) //if the block is not active
        $(this).data('left', $(this).position().left); //sets its left
        $(this).data('top', $(this).position().top);   // and top position
        $(this).animate( //animate at the top and bottom
            top:0,
            left:0
        ,'slow');

        $(this).data('active',true);

    else

        $(this).animate( //animate to its last known position
            top:$(this).data('top'),
            left:$(this).data('left')
        ,'slow');

        $(this).data('active',false);
    
);

演示

Demo[Full](调整大小以查看保持的流动性) Demo[Full](显示可变高度的版本)

以下是此解决方案所提供的:

记住最后一个位置并逐渐动画到/从该位置开始 在加载和每次调整大小时都会计算块位置并设置动画 重定位发生在$(window).resize(),因此保持块的流动性,尽管使用了绝对位置 支持可变高度 对现有标记和 CSS 的微小更改

还修复了 Gaby 扩展的两个问题

每个区块边距的独立帐户 调整大小后重新计算元素的位置

【讨论】:

这个实现的一些问题:a)如果你调整浏览器的大小(以改变布局的方式 ) 隐藏元素后会变得混乱。 b)你没有考虑应用类的边距.. 复制步骤。 1) 调整浏览器大小,让您看到 2 向上和 2 向下 2) 点击第三块 3) 调整浏览器大小(问题1,可见块移动到错误位置4) 调整大小浏览器,以便布局会发生变化(使其非常宽,以便所有内容都出现在 1 行中5)重新单击第 3 个块(问题2,点击的元素移动到最后一个位置-不考虑重新布局) @GabyakaG.Petrioli,修复了这两个问题。谢谢你的建议。您还有什么要评论的吗? 它似乎仍然有错误的重定位问题.. 看我在screencast.com/t/wciBP1mlHape做的截屏视频 我很想听听一些关于否决票的 cmets 以改进我的答案。请务必包含它们【参考方案2】:

最终更新

这是一个完整的工作解决方案(在我看来非常简单),使用 JS 设置定位(一个简单的计算)和其余的 CSS 转换。

演示地址 http://jsfiddle.net/gaby/pYdKB/3/

它保持float:left 的流动性并适用于任意数量的元素,您可以保留:nth-child 进行样式设置,如果您想让多个元素可见,它也可以工作。.

javascript

var wrapper = $('.wrapper'),
    boxes = wrapper.children(),
    boxWidth = boxes.first().outerWidth(true),
    boxHeight = boxes.first().outerHeight(true); 

function rePosition()
    var w = wrapper.width(),
        breakat = Math.floor( w / boxWidth ); // calculate fluid layout, just like float:left

    boxes
        .filter(':not(.go)')
        .each(function(i)
            var matrixX = ((i)%breakat)+1,
                matrixY = Math.ceil((i+1)/breakat);

            $(this).css(
                left:(matrixX-1) * boxWidth ,
                top: (matrixY-1) * boxHeight
            );
        );


$('.box').click(function()
    $(this)
        .siblings()
        .toggleClass('go');// just add the go class, and let CSS handle the rest

    rePosition(); // recalculate final positions and let CSS animate the boxes
);

$(window).resize(rePosition);
$(window).trigger('resize');

CSS

.wrapper
    position:relative;


.box
    width:200px;
    height:100px;
    position:absolute;
    margin:5px;
    cursor:pointer;
    overflow:hidden;
    text-align: center;
    line-height: 100px;

        -moz-transition-property: top,left,width,height;
     -webkit-transition-property: top,left,width,height;
         -ms-transition-property: top,left,width,height;
          -o-transition-property: top,left,width,height;
             transition-property: top,left,width,height;

        -moz-transition-duration: 1s;
     -webkit-transition-duration: 1s;
         -ms-transition-duration: 1s;
          -o-transition-duration: 1s;
             transition-duration: 1s;


.go
    height:0;
    width:0;

注意:正如 @Athari 在 cmets 中正确提到的那样,您应该包括所有浏览器前缀以获得最广泛的支持。 (我最初的回答只包括 moz / webkit 和标准)


原答案

你不能直接用你当前的 html 结构来做。浮动概念不支持。

但如果你能负担得起额外的包装,那没问题..

只需滑动额外包装元素的内容..

float代码放在包装元素上并使用

$(document).ready(function() 

    $(".block-wrapper").click(function() 
        $(this).siblings().find('.block').slideToggle("slow");
    );

);

演示地址 http://jsfiddle.net/gaby/t8GNP/


更新 #1

如果您需要将点击的元素移动到左上角和后退,那么您无法真正使用 CSS。

您需要手动定位它们(​​通过 JS),设置 CSS 过渡(或 jquery),并在单击后应用新位置。

稍后您可能希望不止一个保持可见并重新定位..

所以你可能想看看很棒的Isotope plugin,它可以处理这个问题以及更多的情况/布局

【讨论】:

盒子大小不一样怎么办?您的最终版本不适用于可变框尺寸 @Starx,不,它没有,但 OP 并没有要求这样做..(虽然扩展并不难,只是计算量更大 ) 看起来不错,但困扰我的是您只使用了 -moz- 和 -webkit- 前缀。您也应该始终添加 -o- 和 -ms-。 @Athari,你完全正确.. 它只是一个快速原型.. 但是对于生产,必须针对所有浏览器..(一个方便的插件是leaverou.github.com/prefixfree i>) @mrN :标准、硬件加速、轻量级、不依赖于 javascript、3D。但是后备(关键部分)始终是设计网站的好方法..【参考方案3】:

这是我的版本: http://jsfiddle.net/selbh/dhcyA/92/ (仅更改了 javascript,并且它是响应式的)

$(document).ready(function() 

    $(".block").click(function() 
        var $this = $(this);
        var pos = $this.offset();
        var $siblings = $(this).siblings().add(this);
        var marginTop = $this.css('marginTop').replace(/[^-\d\.]/g, '');
        var marginLeft = $this.css('marginLeft').replace(/[^-\d\.]/g, ''); 

        var $clone = $this.clone();

        $siblings.slideToggle("slow");

        $clone.css(
            position: 'absolute',
            left: pos.left - marginLeft,
            top: pos.top - marginTop,
            'background-color': $this.css('background-color')
        );

        $('body').append($clone);

        $this.css('opacity', 0);

        $clone.animate(
            'left': 0,
            'top': 0
        );


        $clone.click(function() 
            $siblings.slideToggle("slow", function() 
                $clone.remove();
                $this.css('opacity', 1);
            );

            $clone.animate(
                left: pos.left - marginLeft,
                top: pos.top - marginTop
            );
        );


    );

);​

【讨论】:

+1 即使已经有更好的解决方案(如果您单击解决方案中的一个按钮,然后调整窗口大小并再次单击,该按钮将返回到之前的位置,而不考虑当前流程) 【参考方案4】:

我有点困(这里是凌晨 2:30)所以我把一半完成的答案留在这里给你一个想法(我在 30 分钟内完成了,所以我想再多 30 分钟你就能得到一些非常好的东西)

http://jsfiddle.net/LuL2s/2/

诀窍在于制作缓动动画并在它们出现和消失时产生差异的块持有者

JS

$(document).ready(function() 
    var open = true;
    $(".block").click(function() 
            var $this = $(this);
            var count = 0;
        if (open) 
            $this.parent().siblings().children().slideToggle("slow", function()
                if (count++ == 2)                  
                    $this.parent().siblings().animate(width: 'toggle', height:'toggle');
                
            );
         else 
            $this.parent().siblings().animate(width: 'toggle', height:'toggle', function()
                if (count++ == 2)                  
                    $this.parent().siblings().children().slideToggle("slow");                
                
            );

        
        open = !open;                      
    );

);

HTML

<div class="block-holder">
    <div class="block">
        <h2>I'm block 1</h2>
    </div>
</div>

<div class="block-holder">
    <div class="block">
        <h2>I'm block 2</h2>
    </div>
</div>

<div class="block-holder">
    <div class="block">
        <h2>I'm block 3</h2>
    </div>
</div>

<div class="block-holder">
    <div class="block">
        <h2>I'm block 4</h2>
    </div>
</div>

CSS

.block 
    width: 100%;
    height: 100%;
    text-align: center;
    line-height: 100px;
    cursor: pointer;

.block-holder:nth-child(1) .block 
    background: green;

.block-holder:nth-child(2) .block 
    background: red;

.block-holder:nth-child(3) .block 
    background: orange;

.block-holder:nth-child(4) .block 
    background: pink;


.block-holder 
    width: 200px;
    height: 100px;
    float: left;
    margin: 20px;

【讨论】:

+1 努力,即使您的小提琴对于第二行的按钮效果不佳(不是在隐藏按钮时更改窗口大小)【参考方案5】:

伟大的挑战!

新版本:

这是一个更好的版本,因为它使块保持在它们的行中。我添加了一个css 函数,以便您的nth-child 样式甚至可以应用于行中。甚至保持相同的 HTML 结构。

演示: http://jsfiddle.net/MadLittleMods/fDDZB/23/

这个新版本的 jQuery 看起来像:

$('.block').on('click', function() 
    var block = $(this);

    // Keep the blocks in line
    makeRows($('body'));

    $('.block').not(this).each(function() 

        // If sibling on the same level, horizontal toggle
        // We also want ignore the toggleMethod if it is shown because we might need to reassign
        if (($(this).position().top == block.position().top && (($(this).data('toggle') == -1) || $(this).data('toggle') == null)) || ($(this).data('toggle') != -1 && $(this).data('toggleMethod') == 'side'))
        
            $(this).data('toggleMethod', 'side');

            // Hide block
            if ($(this).data('toggle') == -1 || $(this).data('toggle') == null)
            
                // Set properties for later use in show block
                $(this).data('overflowBefore', $(this).css('overflow'));
                $(this).css('overflow', 'hidden');
                $(this).data('marginBefore', $(this).css('margin'));

                var width = $(this).width();
                $(this).animate(
                    width: 0,
                    margin: 0
                , function() 
                    $(this).data('toggle', width);
                );
            
            // Show block
            else
            
                $(this).css('overflow', $(this).data('overflowBefore'));

                $(this).animate(
                    width: $(this).data('toggle'),
                    margin: $(this).data('marginBefore')
                , function() 
                    $(this).data('toggle', -1);
                );
            
        
        // Do a normal vertical toggle
        else 
        
            $(this).data('toggleMethod', 'top');
            $(this).slideToggle('slow');
        
    );

);

// Make rows to make the blocks in line
function makeRows(container)

    // Make rows so that the elements stay where they should
    var containerWidth = container.width();
    var currentRowWidth = 0;

    // Add styles first so nothing gets messed up
    container.children().each(function() 
        var itemCSS = css($(this));
        $(this).css(itemCSS);
    );

    // Now assemble the rows
    container.children().each(function() 

        var blockWidth = $(this).outerWidth() + parseInt($(this).css('margin-left')) + parseInt($(this).css('margin-right'));

        if((currentRowWidth + blockWidth) < containerWidth)
        
            currentRowWidth += blockWidth;
        
        else
        
            Array.prototype.reverse.call($(this).prevUntil('.row')).wrapAll('<div class="row"></div>');
            $(this).prev().append('<div class="row_clear" style="clear: both;"></div>');

            currentRowWidth = 0;
         
    );    


// Remove the rows added
function deleteRows()

    var content = $('.row').contents()
    $('.row').replaceWith(content);

    $('.row_clear').remove();


$(window).resize(function() 
    deleteRows();
);



// Functions courtesy of marknadal
// https://***.com/a/5830517/796832
function css(a)

    var sheets = document.styleSheets, o = ;
    for(var i in sheets) 
        var rules = sheets[i].rules || sheets[i].cssRules;
        for(var r in rules) 
            if(a.is(rules[r].selectorText)) 
                o = $.extend(o, css2json(rules[r].style), css2json(a.attr('style')));
            
        
    
    return o;


function css2json(css)

    var s = ;
    if(!css) return s;
    if(css instanceof CSSStyleDeclaration) 
        for(var i in css) 
            if((css[i]).toLowerCase) 
                s[(css[i]).toLowerCase()] = (css[css[i]]);
            
        
     else if(typeof css == "string") 
        css = css.split("; ");          
        for (var i in css) 
            var l = css[i].split(": ");
            s[l[0].toLowerCase()] = (l[1]);
        ;
    
    return s;

我添加了makeRowsdeleteRows 函数,以便块保持在它们的行中,而不是变小并移动到上面的行中。每当窗口调整大小时,我都会调用deleteRows,以便它可以保持响应式布局。然后,如果需要切换块,我会重新创建行。

csscss2json 函数由 marknadal 提供



旧版本:

我想出了一个使用.animate 的解决方案,这样它就可以水平缓解。

这是一个演示: http://jsfiddle.net/MadLittleMods/fDDZB/8/

jQuery 看起来像:

$('.block').on('click', function() 
    var block = $(this);
    $(this).siblings().each(function() 
        // If sibling on the same level, horizontal toggle
        // We also want ignore the toggleMethod if it is shown because we might need to reassign
        if (($(this).position().top == block.position().top && ($(this).data('toggle') == -1) || $(this).data('toggle') == null) || ($(this).data('toggle') != -1 && $(this).data('toggleMethod') == 'side'))
        
            $(this).data('toggleMethod', 'side');

            // Hide block
            if ($(this).data('toggle') == -1 || $(this).data('toggle') == null)
            
                // Set properties for later use in show block
                $(this).data('overflowBefore', $(this).css('overflow'));
                $(this).css('overflow', 'hidden');
                $(this).data('marginBefore', $(this).css('margin'));

                var width = $(this).width();
                $(this).animate(
                    width: 0,
                    margin: 0
                , function() 
                    $(this).data('toggle', width);
                );
            
            // Show block
            else
            
                $(this).css('overflow', $(this).data('overflowBefore'));

                $(this).animate(
                    width: $(this).data('toggle'),
                    margin: $(this).data('marginBefore')
                , function() 
                    $(this).data('toggle', -1);
                );
            
        
        // Do a normal vertical toggle
        else 
        
            $(this).data('toggleMethod', 'top');
            $(this).slideToggle('slow');
        
    );
);​

关键是将使用.slideToggle.animate 切换的块分开,因为您必须在它们显示和隐藏时应用相同的块。

【讨论】:

以上是关于当其他元素消失时,动画/缓和元素的位置的主要内容,如果未能解决你的问题,请参考以下文章

当列表填满屏幕时,列表下面的android元素消失了

新渲染元素的动画,但不在页面加载时

IE9 和 Z-index:元素在悬停时消失在父级后面

Win10系列:JavaScript动画3

单击时删除 SVG 元素

使用 Prototype 使元素出现/消失