Отмена отложенного setTimeouts для нескольких элементов в цикле for - PullRequest
0 голосов
/ 15 мая 2019

Я пытаюсь создать маленькое крутое микро-взаимодействие, но у меня небольшая проблема.

document.querySelector('button').onclick = function(){
  const
    items = document.querySelector('nav').children
  if (items[0].getBoundingClientRect().top >= document.querySelector('nav').getBoundingClientRect().bottom){
    // start showing elements, starting from the left side
    for (let i = 0; i < items.length; i++){
      setTimeout(function(){
        items[i].style.transform = 'translateY(0)'
      }, i * 200)
    }
  } else {
    // start hiding elements, starting from the right side
    for (let i = 0; i < items.length; i++){
      setTimeout(function(){
        items[i].style.transform = 'translateY(100%)'
      }, (items.length-1 - i) * 200)
    }
  }
}
button {
  width: 100px;
  height: 50px;
}

nav {
  width: 50vw;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-gap: 10px;
  background: red;
}

nav > a {
  width: 100%;
  height: 50px;
  transition: .5s transform;
  transform: translateY(100%);
  opacity: 0.5;
  background: lime;
}
<button>Toggle</button>
<nav>
  <a href=''></a>
  <a href=''></a>
  <a href=''></a>
  <a href=''></a>
</nav>

Если вы включите слишком быстро последовательность, некоторые элементы будут в конечном итоге отображаться, тогда как другие в конечном итоге будут скрыты.

Это связано с тем, что существуют ожидающие setTimeouts, которые еще должны быть выполнены при выпуске нового набора setTimeouts.

Очевидно, что есть способы обойти эту проблему, например, не отменить порядоканимации, ожидая, пока анимация полностью не закончится, прежде чем разрешить обратное, и так далее, но я бы предпочел не идти на такие компромиссы.

Я пытался использовать и переключать глобальное логическое значение в ifelse блоков, а затем с помощью дополнительного оператора if/else в блоке setTimeout, но это не сработало.

Я также пытался установить задержки transition на лету перед применением нового transform значений, вместо того, чтобы полагаться на setTimeout, который не работал.

Есть ли простой способ отменить или игнорировать любые ожидающие setTimeouts из старше цикла?

Ответы [ 2 ]

2 голосов
/ 15 мая 2019

Я бы упростил вашу логику и рассмотрел бы transition-delay, где вам нужно только переключить класс.Хитрость заключается в том, чтобы иметь другую задержку для ваших элементов, когда мы переключаем класс, чтобы получить желаемый эффект.

С этой конфигурацией у вас не будет никаких проблем, потому что все элементы будут иметь одинаковое состояние, так как классдобавляется к их родительскому элементу.

var nav = document.querySelector('nav');
document.querySelector('button').onclick = function(){
  nav.classList.toggle('top');
}
button {
  width: 100px;
  height: 50px;
}

nav {
  width: 50vw;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-gap: 10px;
  background: red;
  --d:0.2s;
}

nav > a {
  width: 100%;
  height: 50px;
  transition: .5s transform;
  transform: translateY(100%);
  opacity: 0.5;
  background: lime;
}
nav.top > a {
  transform: translateY(0);
}

nav > a:nth-last-child(1) { transition-delay:calc(0 * var(--d));}
nav > a:nth-last-child(2) { transition-delay:calc(1 * var(--d));}
nav > a:nth-last-child(3) { transition-delay:calc(2 * var(--d));}  
nav > a:nth-last-child(4) { transition-delay:calc(3 * var(--d));}

nav.top > a:nth-child(1) { transition-delay:calc(0 * var(--d));}
nav.top > a:nth-child(2) { transition-delay:calc(1 * var(--d));}
nav.top > a:nth-child(3) { transition-delay:calc(2 * var(--d));}  
nav.top > a:nth-child(4) { transition-delay:calc(3 * var(--d));}
<button>Toggle</button>
<nav>
  <a href=''></a>
  <a href=''></a>
  <a href=''></a>
  <a href=''></a>
</nav>

Мы можем упростить код CSS, сгруппировав элементы с одинаковой задержкой:

var nav = document.querySelector('nav');
document.querySelector('button').onclick = function(){
  nav.classList.toggle('top');
}
button {
  width: 100px;
  height: 50px;
}

nav {
  width: 50vw;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-gap: 10px;
  background: red;
  --d:0.2s;
}

nav > a {
  width: 100%;
  height: 50px;
  transition: .5s transform;
  transform: translateY(100%);
  opacity: 0.5;
  background: lime;
}
nav.top > a {
  transform: translateY(0);
}

nav > a:nth-last-child(1),
nav.top > a:nth-child(1) { transition-delay:calc(0 * var(--d));}

nav > a:nth-last-child(2),
nav.top > a:nth-child(2) { transition-delay:calc(1 * var(--d));}

nav > a:nth-last-child(3),
nav.top > a:nth-child(3){ transition-delay:calc(2 * var(--d));}  

nav > a:nth-last-child(4),
nav.top > a:nth-child(4){ transition-delay:calc(3 * var(--d));}
<button>Toggle</button>
<nav>
  <a href=''></a>
  <a href=''></a>
  <a href=''></a>
  <a href=''></a>
</nav>
0 голосов
/ 15 мая 2019

Этот ответ показывает хороший способ очистить все setTimeout - просто добавьте его в каждую часть оператора if/else:

document.querySelector('button').onclick = function() {
    const
      items = document.querySelector('nav').children

    if (items[0].getBoundingClientRect().top >= document.querySelector('nav').getBoundingClientRect().bottom) {
      var id = window.setTimeout(() => {}, 0);
      while (id--) {
        window.clearTimeout(id);
      }
      // start showing elements, starting from the beginning
      for (let i = 0; i < items.length; i++) {
        setTimeout(function() {
          items[i].style.transform = 'translateY(0)'
        }, i * 200)
      }
    } else {
      var id = window.setTimeout(() => {}, 0);
      while (id--) {
        window.clearTimeout(id);
      }
        // start hiding elements, starting from the back
        for (let i = 0; i < items.length; i++) {
          setTimeout(function() {
            items[i].style.transform = 'translateY(100%)'
          }, (items.length - 1 - i) * 200)
        }
      }
    }
button {
  width: 100px;
  height: 50px;
}

nav {
  width: 50vw;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-gap: 10px;
  background: red;
}

nav>a {
  width: 100%;
  height: 50px;
  transition: .5s transform;
  transform: translateY(100%);
  opacity: 0.5;
  background: lime;
}
<button>Toggle</button>
<nav>
  <a href=''></a>
  <a href=''></a>
  <a href=''></a>
  <a href=''></a>
</nav>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...