Alexanevsky
@Alexanevsky
Любительская web-разработка

Как постепенно выполнять действия, а потом всё повторять?

Здравствуйте!

Проблема, как мне кажется, очень простая.

Есть следующая структура:
<div class="block-1"></div>
<div class="block-2"></div>
<div class="block-3"></div>

Необходимо сделать всё по примерно такому алгоритму:
  1. Добавить для block-1 класс active;
  2. Через 500 мс. - добавить для block-2 класс active;
  3. Через 400 мс. - добавить для block-3 класс active;
  4. Через 300 мс. - снять со всех блоков классы active и запустить всё заново.

Есть ряд проблем, с которыми я столкнулся при попытке сделать что-то подобное.

На каждое действие отводится какое-то количество мс., так как это своего рода анимация, т.е., например:
.block-1 имеет стили height: 0; transition: height .5s linear;
а .block-1.active имеет height: 100px;
Получается, что за 0.5s высота блока должна стать 100px.

Я пытался добавить delay, т.е.
$('.block-1').addClass('active');
$('.block-2').delay(500).addClass('active');
$('.block-3').delay(900).addClass('active');
...

Но delay почему-то не хочет работать с addClass. Тогда я использовал не addClass, a animate, т.е.
$('.block-1').animate({height: 100px}, 500);
$('.block-2').delay(500).animate({height: 100px}, 400);
$('.block-3').delay(900).animate({height: 100px}, 300);
...

Тем самым, я вообще убрал класс active и всё делал сразу через JS. Всё работает, но есть одна проблема. Мне необходимо именно addClass, а не animate. Это вызвано необходимостью адаптировать всё это дело под разные размеры экрана, т.е. примерно по такой схеме:
.block-1.active {height: 100px;}
@media (max-width: 1000px) {
      .block-1.active {height: 50px;}
}
@media (max-width: 700px) {
      .block-1.active {height: 10px;}
}

И т.п.
Адаптировать просто в CSS, не трогая JS, гораздо проще, чем вносить изменения в сам скрипт.

Вторая проблема - это delay. Delay ведёт свой отчёт от самого начала, а не от предыдущего delay. Т.е. чтобы каждое действие происходило через несколько мс после предыдущего, мне надо делать так:
  1. Первое действие;
  2. Второе действие - delay: 500 ms;
  3. Третье действие - delay: 900 ms;
  4. Четвёртое действие - delay: 1200 ms.

В таком виде проблемы нет, но это просто пример кода и на самом деле у меня всё гораздо интереснее. Когда более 40 элементов в заданной последовательности изменяются, вносить какие-то изменения в скрипт (особенно в самое начало) повлечет за собой кучу проблем.

И всё это нужно повторять после того, как свершится последнее действие.

Скажите пожалуйста, как всё это можно сделать?

Спасибо!

UPD: решение проблемы с учётом ответов и помощи зарубежного комьюнити:

Всё работает как надо, скрипт запускается без задержек.
<div class="block block-1"></div>
<div class="block block-2"></div>
<div class="block block-3"></div>

.block-1 {background: red;}
.block-1.active {height: 100px;}
.block-2 {background: green;}
.block-2.active {height: 100px;}
.block-3 {background: blue;}
.block-3.active {height: 100px;}

.block-1.active {transition: height .5s linear;}
.block-2.active {transition: height .5s linear; transition-delay: .5s;}
.block-3.active {transition: height .5s linear; transition-delay: 1s;}
.block {transition: height 0s linear;}

function animation() {
    $('.block-1').addClass('active');
    $('.block-2').addClass('active');
    $('.block-3').addClass('active');
    setTimeout(function() {
        $('.block-1, .block-2, .block-3').removeClass('active');
    }, 1500);
}

animation();

setInterval(function() {
    animation();
}, 1500);

Оно же на JSFiddle.
  • Вопрос задан
  • 2388 просмотров
Решения вопроса 1
Ronnie_Gardocki
@Ronnie_Gardocki
Я у мамы фронтендщик.
Варианта два:
1) Добавлять ко всем блокам классы одновременно, и ставить для каждого класса свою задержку на transition. Пример:
.block-1.active {transition: height %time%;}
.block-2.active {transition: height %time% 500;}
.block-3.active {transition: height %time% 900;}
.block-4.active {transition: height %time% 1200;}
/* как вариант, transition: %property% %time% можно задать для всех одно, а по отдельности менять только свойство transition-delay, если вам так удобнее будет */

Если вам при снятии класса требуется, чтобы анимация всех блоков произошла одновременно, то для их общего класса (ну или для каждого по отдельности) пишите просто что-нибудь такое:
.block {transition: height %time% 0;}
В итоге если класс active у блока есть, его анимация стартует с задержкой (первая после свойства это время исполнения, а вторая это задержка). Если класса .active нету, то и задержки не будет.
2) Второй вариант предполагает использование setTimeout(){function() {%действие%}, %delay%) для каждого действия с делеем. Если положить таймаут внутрь другого, то его время сложится с родителем (то есть его таймаут стартнет только когда "выстрелит" таймаут родителя).

Ну и для цикличности все это дело надо просто упаковать в setInterval.
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
standy
@standy
jquery в плане сложной асинхронной логики это плохой помощник, смотрите в сторону promises

Подождать 500мс можно так:
setTimeOut(function() {
  $('.block-2').addClass('active');
  // тут запускаете следующее ожидание
}, 500)
Ответ написан
Комментировать
thewind
@thewind
php программист, front / backend developer
Если что, то у animate есть callback, где как раз можно сделать addClass. А сам animate сделать фейковым ( просто ненужное css свойство). К сути проблемы это врядли имеет отношение, просто совет к вашему методу.
Ответ написан
Комментировать
@dtestyk
возможно, такой вариант вам подойдет
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы