使用有限元素列出旋转

时间:2015-10-29 10:50:40

标签: javascript html html5 css3 translate-animation

我的div container里面有列表(卡片)。当我将鼠标悬停时,卡片开始移动(translateX animation)。 container的{​​{1}}为width,元素计入300px,每个元素container:3

所以你可以在容器中看到3个元素width:100px。我要做的是,当没有元素在第三个元素之后显示 translateX animation -100px = 100px 空格时,它会在列表中的1个元素后面立即开始,没有空白空间。

目前,我不知道如何在没有重复等的情况下完成。

这就是我现在所拥有的: Fiddle(用于查看翻译动画的悬停卡片)

UPD 1: 以代码和数据(卡数,容器大小)为例,我将尝试更好地解释我想要的东西:我的目标是建立卡片列表,按下按钮后,列表将开始移动(如示例中)使用translateX动画)一段时间(例如translateX: 12491px ,动画持续时间: 15s ;)并停止。但问题是列表中的碎片数量将在3-40张卡的范围内(每张卡的宽度和高度均为100px)。因此,当我设置 translateX: 12491px 时,它将超出范围,并且在列表中的最后一张卡出现空白后。我希望第一张牌和最后一张牌以某种方式被捆绑,并且在最后一张牌之后立即出现在列表中的第一张牌等等。也许我正在以错误的方式寻找解决方案,但我想你理解主要想法。

UPD 2: 我发现cs:go使用我想在html \ css \ js上写的动画。这是视频:youtube.com

HTML:

overflow:hidden

的CSS:

<div class="container">
    <div class="cards">
        <div class="card">
        1
    </div>
    <div class="card">
        2
    </div>
    <div class="card">
        3
    </div>
    </div>
</div>

6 个答案:

答案 0 :(得分:6)

  

从最后一个列表中的1个元素开始,没有   空白

这超出了CSS,你需要Javascript。因为,你用Javascript而不是jQuery标记了这个问题,我的答案仅限于纯Javascript。看看ma,没有JQuery;)

  

我不知道如何在没有重复的情况下完成它

这是一个DIY(自己动手)的想法..

  1. 主要技巧是至少显示一项少于你总数的项目。如果您有3张牌,则仅显示2.如果您有4张牌,则仅显示3.为什么,因为当牌退出视图时需要重新定位牌并在最后将其换回。如果您显示的卡数与您所拥有的卡数完全相同,那么您无法打破半卡并将其包裹起来,您将看到一些空白区域,直到第一张卡片脱离视线。你明白了吗?
  2. 不要使用translate,否则在编写脚本时最终会使事情变得复杂。保持简单。
  3. 请勿使用包装纸。为什么?因为,我们将重新定位已经消失的卡片。当我们这样做时,下一张牌就会取而代之,立即离开视线,让你的事情变得更加困难。
  4. 为简单起见,请安排相对于其容器absolute定位的卡片。首先,让所有卡片都堆放在top:0; and left: 0;
  5. 接下来连接Javascript以根据每张卡片的left定位width属性并线性排列。
  6. 使用requestAnimationFrame来控制动画。
  7. 跟踪最左侧的卡片及其left位置。当这不在视野范围内时( 0减去宽度),appendChild此卡到其容器。这会将卡片移动到卡片的末尾。另外,根据列表中的最后一张卡将left属性更改为该属性。
  8. 也就是说&#39;一切都有。
  9. 以下是演示。为了方便您进行实验,我使用了一个设置对象来保存您可以轻松调整和查看的可配置属性。仔细查看代码,您会发现它很容易理解。您可以将iterations设置为0以使动画无限。

    另外,请注意,您无需复制或伪造卡片。试试这个演示并添加你想要的卡片。

    代码段中的内联代码注释将进一步帮助您理解每行代码并与上述步骤相关。

    <强>段:

    &#13;
    &#13;
    var list = document.querySelector('.cardList'), // cache the container
        cards = document.querySelectorAll('.card'), // cache the list of cards
        start = document.getElementById('start'),   // buttons
        stop = document.getElementById('stop'), 
        reset = document.getElementById('reset'), 
        raf, init = 0, counter = 0, lastCard, currentIteration = 0, // general purpose variables
        settings = { // settings object to help make things configurable
            'width': 100, 'height': 100, 'speed': 2, 
            'iterations': 2, 'count': cards.length 
        }
    ;
    start.addEventListener('click', startClick); // wire up click event on buttons
    stop.addEventListener('click', stopClick);
    reset.addEventListener('click', resetClick);
    initialize(); // initialize to arrange the cards at start
    
    function initialize() {
        // loop thru all cards and set the left property as per width and index position
        [].forEach.call(cards, function(elem, idx) { 
            elem.style.left = (settings.width * idx) + 'px';
        }); 
        init = -(settings.width); // initialize the view cutoff
        lastCard = cards[settings.count - 1]; // identify the last card
        counter = 0; currentIteration = 0; // reset some counters
        settings.speed = +(document.getElementById('speed').value);
        settings.iterations = +(document.getElementById('iter').value);
    }
    function startClick() { 
        initialize(); raf = window.requestAnimationFrame(keyframes); // start animating
    }
    function stopClick() { window.cancelAnimationFrame(raf); } // stop animating
    function resetClick() { // stop animating and re-initialize cards to start again
        window.cancelAnimationFrame(raf); 
        document.getElementById('speed').value = '2';
        document.getElementById('iter').value = '2';
        initialize(); 
    }
    
    // actual animation function
    function keyframes() {
        var currentCard, currentLeft = 0, newLeft = 0;
        // iterate all cards and decrease the left property based on speed
        [].forEach.call(cards, function(elem, idx) {
            elem.style.left = (parseInt(elem.style.left) - settings.speed) + 'px';
        }); 
        currentCard = cards[counter]; // identify left-most card
        currentLeft = parseInt(currentCard.style.left); // get its left position
        if (currentLeft <= init) { // check if it has gone out of view
            // calculate position of last card
            newLeft = parseInt(lastCard.style.left) + settings.width;
            list.appendChild(currentCard); // move the card to end of list
            currentCard.style.left = newLeft + 'px'; // change left position based on last card
            lastCard = currentCard; // set this as the last card for next iteration
            counter = (counter + 1) % settings.count; // set the next card index
            if ((settings.iterations > 0) && (counter >= (settings.count - 1))) { 
                currentIteration++; // check settings for repeat iterations
            }
        }
        if (currentIteration >= settings.iterations) { return; } // when to stop
        raf = window.requestAnimationFrame(keyframes); // request another animation frame
    };
    &#13;
    * { box-sizing: border-box; padding: 0; margin: 0; }
    .cardList { 
        position: relative; height: 100px; width: 300px; 
        margin: 10px; border: 2px solid #33e; 
        overflow: hidden; white-space: nowrap; 
    }
    .card { 
        position: absolute; left: 0; top: 0; text-align: center;
        height: 100px; width: 100px; line-height: 100px;
        background-color: #99e; 
        font-family: monospace; font-size: 2em; color: #444;
        border-left: 1px solid #33e; border-right: 1px solid #33e;
    }
    
    div.controls, button { margin: 10px; padding: 8px; font-family: monospace; }
    div.controls input { width: 48px; padding: 2px; text-align: center; font-family: monospace; }
    &#13;
    <div class="controls">
        <label>Speed <input id="speed" type="number" min="1" max="8" value="2" />x</label>
        &nbsp;|&nbsp;
        <label>Iterations <input id="iter" type="number" min="0" max="8" value="2" /></label>
    </div>
    <div class="cardList">
        <div class="card">1</div>
        <div class="card">2</div>
        <div class="card">3</div>
        <div class="card">4</div>
    </div>
    <button id="start">Start</button>
    <button id="stop">Stop</button>
    <button id="reset">Reset</button>
    &#13;
    &#13;
    &#13;

    小提琴:http://jsfiddle.net/abhitalks/1hkw1v0w/

    注意: 我在演示中遗漏了一些内容。特别是,虽然卡片的宽度和高度是设置对象的一部分,但目前它是固定的。您可以轻松使用设置对象来使卡的尺寸也可配置。

    编辑:

    根据Op的评论

    如果你想更好地控制距离滚动,持续时间和计时功能(缓和),那么你可以自己使用库实现这些。 Robert Penner's Easing Functionsa jQuery plugin from GSGD是一些好的库。虽然你可以用纯Javascript实现所有这些,但如果你使用像jQuery这样的库会更容易。

    这里要抓住的是,为了有效地这样做,你必须复制这些卡片。你可以通过几次克隆整个列表来轻松完成。

    虽然你没有用jQuery标记这个问题,但这是一个小型演示(使用jQuery快速完成),你可以在其中配置速度和距离。

    代码段2:

    &#13;
    &#13;
    var $cardList 	= $('.cardList').first(), 
        $cards 		= $('.card'), 
        $speed 		= $('input[name=speed]'), 
        width 		= 100, 
        randomize 	= true, 
        distance 	= 20 * width 
    ;
    
    for (var i = 0; i < 50; i++) {
        $cards.clone().appendTo($cardList);
    }
    
    function spin() {
        var newMargin = 0, newDistance = distance, 
            speed = +($speed.filter(':checked').val());
        if (randomize) {
            newDistance = Math.floor(Math.random() * $cards.length * 5);
    		newDistance += $cards.length * 5;
            newDistance *= width;
        } 
    	newMargin = -(newDistance);
        $cards.first().animate({
            marginLeft: newMargin
        }, speed);
    }
    
    $('#spin').click(function() {
        $cards.first().css('margin-left', 0);
        spin();
        return false;
    });
    &#13;
    * { box-sizing: border-box; padding: 0; margin: 0; }
    .cardList { 
        height: 100px; width: 302px; position: relative;
        margin: 10px; border: 1px solid #33e; 
        overflow: hidden; white-space: nowrap; 
    }
    .card { 
        display: inline-block; text-align: center;
        height: 100px; width: 100px; line-height: 100px;
        background-color: #99e; 
        font-family: monospace; font-size: 2em; color: #444;
        border-left: 1px solid #33e; border-right: 1px solid #33e;
    }
    .cardList::before, .cardList::after {
        content: ''; display: block; z-index: 100;
        width: 0px; height: 0px; transform: translateX(-50%);
    	border-left: 8px solid transparent;
    	border-right: 8px solid transparent;    
    }
    .cardList::before {
        position: absolute; top: 0px; left: 50%;
    	border-top: 12px solid #33e;
    }
    .cardList::after {
        position: absolute; bottom: 0px; left: 50%;
    	border-bottom: 12px solid #33e;
    }
    div.controls, button { margin: 10px; padding: 8px; font-family: monospace; }
    div.controls input { width: 48px; padding: 2px; text-align: center; font-family: monospace; }
    &#13;
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <div class="controls">
        <label>Speed: </label>
        &nbsp;|&nbsp;
        <label><input name="speed" type="radio" value='6000' />Slow</label>
        <label><input name="speed" type="radio" value='5000' checked />Medium</label>
        <label><input name="speed" type="radio" value='3000' />Fast</label>
    </div>
    <div class="cardList"><!--
        --><div class="card">1</div><!--
        --><div class="card">2</div><!--
        --><div class="card">3</div><!--
        --><div class="card">4</div><!--
    --></div>
    <button id="spin">Spin</button>
    &#13;
    &#13;
    &#13;

    小提琴2:http://jsfiddle.net/abhitalks/c50upco5/

答案 1 :(得分:3)

更新2:

我写了一个jquery插件,可以按你想要的方式运行:

你可以根据需要添加任意数量的卡片,现在“translateX”是随机的(脚本将随机选择最终卡片)

link to the demo

<强>更新

我知道,我使用了重复项,但现在我的代码可以使用三张卡:

  • 我添加了三张“假”卡
  • 每张“真实”牌都有自己的动画
  • 一旦他们的周期结束,“假”卡将被真实的卡重叠(“当没有要显示的元素时)

检查代码段:

.container {
  width: 300px;
  height: 100px;
  border: 2px solid black;
  overflow: hidden;
}
.card {
  float: left;
  height: 100px;
  width: 100px;
  background-color: blue;
  box-sizing: border-box;
  border: 2px solid red;
  color: white;
  font-size: 23px;
}
.cards {
  width: 600px;
}
.container:hover .card1{
  animation: 1600ms slide1 infinite linear;
}
.container:hover .card2{
  animation: 1600ms slide2 infinite linear;
}
.container:hover .card3{
  animation: 1600ms slide3 infinite linear;
}
.fakecard{z-index:-1000;}
.container:hover .fakecard{
  animation: 1600ms fakeslide infinite linear;
}

@keyframes slide1 {
  0% { transform: translateX(0px); }
  33% { transform: translateX(-100px); }
  33.1% { transform: translateX(+200px); }
  100% { transform: translateX(0px); }
}
@keyframes slide2 {
  0% { transform: translateX(0px); }
  66% { transform: translateX(-200px); }
  66.1% { transform: translateX(100px); }
  100% { transform: translateX(0px); }
}
@keyframes slide3 {
  0% { transform: translateX(0px); }
  99% { transform: translateX(-300px); }
  99.1% { transform: translateX(+300px); }
  100% { transform: translateX(0px); }
}
@keyframes fakeslide {
  0% { transform: translateX(0px); }
  99% { transform: translateX(-300px); }
  99.1% { transform: translateX(+300px); }
  100% { transform: translateX(0px); }
}
<div class="container">
  <div class="cards">
    <div class="card card1">
      1
    </div>
    <div class="card card2">
      2
    </div>
    <div class="card card3">
      3
    </div>
    <div class="card fakecard">
      1 (fake)
    </div>
    <div class="card fakecard">
      2 (fake)
    </div>
    <div class="card fakecard">
      3 (fake)
    </div>
  </div>
</div>

上一个回答:

这是你想要实现的目标吗?

我认为你不能没有重复......

如果没有,你能更好地解释一下你想要在这里实现的目标吗?

[删除已删除的代码]

答案 2 :(得分:3)

如果您不想修改dom元素,可以利用flex-item's order property;

要做到这一点,你仍然需要一点JS来在动画结束后添加这个属性;

我也改为动画而不是转换,所以它会在动画结束时自动重置转换属性。

&#13;
&#13;
$('.cards').mouseenter(function() {
  setTimeout(function() {
    $('.card').first().css("order", "2");
  }, 3000);
});

$('.cards').mouseleave(function() {
  $('.card').first().css("order", "-1");
});
&#13;
.container {
  width: 300px;
  height: 100px;
  border: 2px solid black;
  overflow: hidden;
}
.card {
  float: left;
  /* height: 100px;
    width: 100px;*/
  background-color: blue;
  box-sizing: border-box;
  border: 2px solid red;
  color: white;
  font-size: 23px;
  flex: 0 0 25%;
}
.cards:hover {
  animation: trans 3s;
}
/**/

.cards {
  width: 400px;
  height: 100%;
  display: flex;
  transition: transform 3s;
}
@keyframes trans {
  0% {
    transform: translateX(0)
  }

  100% {
    transform: translateX(-100px)
  }
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>

<div class="container">
  <div class="cards">
    <div class="card">1</div>
    <div class="card">2</div>
    <div class="card">3</div>
  </div>
</div>
&#13;
&#13;
&#13;

fiddle

但如果您可以使用JS,我建议您直接操作DOM元素的顺序,获取.cards的第一个子元素,并在每个动画结束时将其附加到列表的末尾;

试试这个:

&#13;
&#13;
var anim;

$('.cards').mouseenter(function(){
        
    anim = setInterval(function(){
        $('.cards').append($('.card').first())
    },3000)
    	
});

$('.cards').mouseleave(function(){
    clearInterval(anim)                   
});
&#13;
.container{
    width:300px;
    height: 100px;
    border: 2px solid black;
    overflow: hidden;
}
.card{
    float:left;
   /* height: 100px;
    width: 100px;*/
    background-color:blue;
    box-sizing: border-box;
    border: 2px solid red;
    color: white;
    font-size: 23px;
    /**/
    flex:0 0 25%;
}
.cards:hover{
    animation: trans 3s infinite;
}

/**/
.cards{
    width:400px;
    height:100%;
    display:flex;
}


@keyframes trans {
  0% {
    transform: translateX(0)
  }
  100% {
    transform: translateX(-100px)
  }

}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="container">
    <div class="cards">
        <div class="card">
        1
    </div>
    <div class="card">
        2
    </div>
    <div class="card">
        3
    </div>
    </div>
</div>
&#13;
&#13;
&#13;

如果你想在卡片列表的开头和结尾同时出现一张卡片,你需要对该元素进行深层复制/克隆;

here's an example;

答案 3 :(得分:2)

Here与你提到的效果相同,只需对你的CSS进行一些调整,并从jQuery那里得到帮助。

CSS

更改translateX动画的选择器,以便在其{em>悬停时立即应用于每个.card框,而不是.cards(是.card的直接父母。这是因为您希望向左移动,而不是在进行移动时它们出现的窗口

即,

.cards:hover .card {   
    transform: translateX(-100px);
    transition-duration: 1.5s;
    animation-duration: 1.5s;
    animation-fill-mode: forwards;
}

的jQuery

var $container = $('.container');
var cardWidth = 100;

$container.on('mouseenter', function (e) {
    e.preventDefault();
    var $card0Clone = $('.card').eq(0).clone();    // clone of the first .card element
    $('.cards').append($card0Clone);
    updateWidth();
});
$container.on('mouseleave', function (e) {
    e.preventDefault();
    var $cards = $('.card');
    $cards.eq(0).remove();    // remove the last .card element
});

function updateWidth() {
    $('.cards').width(($('.card').length) * cardWidth);  // no of cards in the queue times the width of each card would result in a container fit enough for all of them
}

代码说明

当您移动鼠标指针时,会创建第一张卡片的 clone ,并附加到卡片集合的末尾。此外,当您将鼠标移出悬停区域时,原始的.card(之前克隆的 )将从队列的头部移除 - 因此产生循环效果。

真正的技巧是使用updateWidth功能。每次鼠标进入.container时,.card s'的直接父级(即.cards div)的宽度都会更新,因此.cards div的宽度足以容纳所有.card,因此,确保每个相互推挤,并在翻译动画时保持一行完成。

答案 4 :(得分:2)

这是一种操作Dom以创建所需效果的简单技术

使用Javascript:

document.querySelector('.cards').addEventListener('mousedown', function(e) {
if (e.clientX < (this.offsetWidth >> 1)) {
    this.appendChild(this.removeChild(this.firstElementChild));
} else {
    this.insertBefore(this.lastElementChild, this.firstElementChild);
}}); 

然后在你的css中使用nth-of-type选择器根据需要定位元素。 这是您的fiddle

如果您使用鼠标悬停,则可能需要等待transitionend事件再次触发之前。

答案 5 :(得分:1)

Check out this demo

在这里,我使用了JQuery,您可以使用两个变量来配置动画

var translateX = 1000; //adjust the whole distance to translate
var stepSpeed = 100;   //adjust the speed of each step transition in milliseconds

设置变量后,在卡片的点击事件中执行以下操作: -

  1. 根据translateX获取所需步骤数
  2. 循环执行步骤数
  3. 在每个循环内(每一步)将卡片向左移动1步,然后将第一张卡片放到卡片的末端以形成连接的循环,然后将卡片返回到它的初始位置< / LI>

    以下是代码:

    var stepsNumber = translateX/100;
    for(var i=0; i< stepsNumber; i++)
    {
        $('.cards').animate({'left' : -100}, stepSpeed,function(){
             $('.cards div:last').after($('.cards div:first'));
             $('.cards').css({'left' : '0px'}); 
         });
    }