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

时间:2012-10-16 08:12:56

标签: javascript jquery jquery-animate easing

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

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

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

对小提琴或小说的任何修改都会很棒!

5 个答案:

答案 0 :(得分:7)

这是我的解决方案。

在你现有的标记上,我添加了一个包装器分区来计算包装器内盒子的位置。喜欢这个

<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()上,因此保持块的流畅性质,尽管使用了绝对位置
  •   
  • 支持变量高度
  •   
  • 对现有标记进行微小更改&amp; CSS
  •   
     

还解决了Gaby扩展的两个问题

     
      
  • 独立计算每个区块保证金
  •   
  • 重新调整大小后元素的位置
  •   

答案 1 :(得分:5)

最终更新

这是一个完整的解决方案(我认为很简单),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在评论中正确提到的那样,您应该包含所有浏览器前缀以获得最广泛的支持。 (我最初的答案只包括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以及更多的情况/布局

答案 2 :(得分: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
            });
        });


    });

});​

答案 3 :(得分:2)

伟大的挑战!

新版本:

这是一个更好的版本,因为它使块保持在他们的行中。我添加了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://stackoverflow.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切换的块分开,因为在显示和隐藏时必须应用相同的块。

答案 4 :(得分:2)

我有点困了(这是凌晨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;
}