Loop contínuo com divs e jQuery
-
20-09-2019 - |
Pergunta
Estou continuando um postagem anterior, mas pensei em abrir um novo tópico, pois é uma abordagem diferente e tem mais código real. De qualquer forma, estou tentando obter um loop infinito com o Divs rolando através de uma janela (a outra postagem tem uma imagem e o código abaixo funciona).
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Bleh...</title>
<style type="text/css" media="screen">
body {
padding: 0;
text-align: center;
}
#container {
width: 1000px;
padding: 10px;
border: 1px solid #bfbfbf;
margin: 0 auto;
position: relative;
}
#window {
width: 400px;
height: 100px;
padding: 10px;
border: 3px solid #666;
margin: 0 auto;
}
.box {
height: 100px;
width: 100px;
border: 1px solid #666666;
position: absolute;
z-index: -1;
}
.red { background-color: #ff6868;}
.green { background-color: 7cd980;}
.blue { background-color: #5793ea;}
.yellow { background-color: #f9f69e;}
.purple { background-color: #ffbffc;}
.cyan { background-color: #bff3ff;}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript" charset="utf-8">
$(window).load(function() {
arrangeBoxes();
setInterval('shiftLeft()', 3000);
});
// arrange the boxes to be aligned in a row
function arrangeBoxes() {
$('.box').each( function(i, item) {
var position = $('#window').position().left + 3 + i * ( $(item).width() + 10 );
$(item).css('left', position+'px')
});
}
// shifts all the boxes to the left, then checks if any left the window
function shiftLeft() {
$('.box').animate({'left' : "-=100px"}, 3000, 'linear');
checkEdge();
}
// returns the new location for the box that exited the window
function getNewPosition() {
return $('.box:last').position().left + $('.box:last').outerWidth() + 10;
}
// if the box is outside the window, move it to the end
function checkEdge() {
var windowsLeftEdge = $('#window').position().left;
$('.box').each( function(i, box) {
// right edge of the sliding box
var boxRightEdge = $(box).position().left + $(box).width();
// position of last box + width + 10px
var newPosition = getNewPosition();
if ( $(box).attr('class').indexOf('red') >=0 ) {
console.log('box edge: ' + boxRightEdge + ' window edge: ' + windowsLeftEdge + ' new pos: ' +newPosition);
}
if ( parseFloat(boxRightEdge) < parseFloat(windowsLeftEdge) ) {
$(box).css('left', newPosition);
$(box).remove().appendTo('#window');
first = $('.box:first').attr('class');
console.log('first is ' + first);
}
});
}
</script>
</head>
<body id="index">
<div id="container">
<div id="window">
<div class="box red"></div>
<div class="box green"></div>
<div class="box blue"></div>
<div class="box yellow"></div>
<div class="box purple"></div>
<div class="box cyan"></div>
</div>
</div>
</body>
</html>
De qualquer forma, o problema é que eu posso fazer com que as caixas se movam para a esquerda, mas não consigo colocar a caixa principal para voltar ao final da linha quando corro a coisa toda. Quando eu executo cada função separadamente (usando o Firebug) -
>> $('.box').animate({'left' : "-=100px"}, 3000, 'linear');
>> checkEdge()
Funciona muito bem. Quando os coloquei, eu só recebo um movimento contínuo da direita para a esquerda até que as caixas saem da tela, então deve ser como elas interagem. Espero que isso faça sentido (se não, salve o bloco de código acima como um arquivo HTML e execute -o em um navegador). Estou preso nisso por um tempo, então qualquer ajuda será incrível. Obrigado.
Solução
Você precisa definir o retorno de chamada como este
$('.box').animate({'left' : "-=100px"}, 3000, 'linear', checkEdge());
checkEdge()
não checkEdge
Verifique aqui uma solução de trabalho http://jsbin.com/uceqi (Basta copiar seu código colado)
Aqui, na verdade, faz a diferença se você deixar o aparelho para longe. A versão com aparelho é executada checkEdge()
Uma vez por animação (para ser honesto na realidade, ele executa checkEdge
Antes que a animação comece. o checkEdge()
A função é chamada quando o $...animte(..)
a declaração é alcançada mesmo antes animate()
Ele próprio é executado. checkEdge
faria com que a função fosse passada como parâmetro, normalmente o caminho correto a seguir).
A versão sem aparelho é executada checkEdge
Uma vez para o número de elementos correspondentes ao seletor. Nesse caso, 6 vezes.
Então, tecnicamente falando, sua abordagem estaria correta, pois dispararia o retorno de chamada uma vez por elemento animado e DEPOIS a animação. Mas executando checkEdge
Uma vez que isso obviamente não é o que o autor pretendia (verifique o loop sobre divs em checkEdge
). E também causa atraso grave ao fazer um loop várias vezes sobre todos os divs
6divs animados ->
6x de retorno de chamada disparado ->
Loops de retorno de chamada sobre todos os divs ==>
36x execução da função anônima em checkEdge()
!!
Portanto, o problema real é que o retorno de chamada é demitido 6x e tudo ao mesmo tempo. Assim, temos uma condição de corrida e a animação estraga tudo. Como vários checkEdge()
chama o trabalho simultaneamente para reposicionar os divs.
Mas você só percebe isso porque o animation-speed
e o intervalo em setInterval
(Shiftleft) são idênticos. Eles seriam diferentes ((intervalo +~ 1000ms)> velocidade da animação), então também funcionaria corretamente. Mas é claro que a animação não seria fluida. Como shitfLeft()
dispararia 1s depois que a animação parou.
Você pode verificar esta amostra http://jsbin.com/iwapi3 que ilustra o que acontece quando use a versão sem aparelho. Espere por algum tempo e o balcão para checkEdge()
As chamadas aumentam em 7 a cada 3s. Você também notará que, em alguns casos, a animação funciona parcialmente e algumas divs são transferidas de volta ao final da lista (às vezes).
Depois de algum tempo, clique no botão e observe como a versão com aparelho resolve a bagunça após algumas execuções (mas agora a ordem correta da cor é obviamente bagunçada). E apenas ligações checkEdge()
Uma vez por animação. (Logo após pressionar o botão, você receberá outros +7, pois alguns retornos de chamada ainda estão no tubo)
Outras dicas
A execução do JavaScript não espera até que os 3000 ms de sua função animada sejam gastos. Ele inicia a função animada, mas então (após 0-1 ms) passará para a função checkedge (). Você não verifica o estado em checkedge () que pode querer.
Para evitar isso, você deve adicionar sua função checkEdge () como retorno de chamada à função animada, que o executará exatamente após esses 3000ms.
Experimente isso:
$('.box').animate({'left' : "-=100px"}, 3000, 'linear', checkEdge);