Chrome на OSX: переходы CSS применяются сразу ко второму клону div

Я хочу сделать следующее: создать div (#test), а затем много раз клонировать этот div, каждый раз, когда он клонируется, добавляя к нему переход css через javascript. Все работает хорошо в первый раз, но если я попытаюсь клонировать второй раз и применить переход css, то переход не сработает.

В этом примере (https://jsfiddle.net/9uL1qt6n/13/) красный квадрат движется как положено, но зеленый квадрат не двигается и появляется в конце перехода мгновенно.

Вот код javascript, который я использую:

function move(color){
  let clone=document.getElementById("test").cloneNode(true);
  clone.id=color;
  clone.style.display="block";
  clone.style.backgroundColor=color;
  document.getElementById("main").prepend(clone);
  setTimeout(function(){
      clone.style.left="500px";
  },0)
}

setTimeout(function(){move("red")},500);
setTimeout(function(){move("green")},750);

Я ожидаю, что красный квадрат будет начинаться с левого = 0 пикселей на 0,5 с и двигаться вправо, а затем зеленый квадрат, который начинается с левого = 0 пикселей на 0,75 с и двигаться вправо. То, что я вижу, это красный квадрат, который начинается с левого = 0 пикселей на 0,5 с и движется вправо, а затем зеленый квадрат, который начинается с левого = 500 пикселей на 0,75 с и не движется.

Изменить: кажется, что это правильно работает в Safari на Mac, а также в Safari и Chrome на iOS. Предлагаемое выше поведение появляется только в Chrome на Mac.


person user1763510    schedule 25.10.2020    source источник


Ответы (1)


Это связано с тем, что setTimeout(/**/, 0) не гарантирует, что обратный вызов будет выполнен в последующем кадре. Что может (в зависимости от реализации браузера и скорости компьютера) привести к тому, что стиль будет применен к тому же фрейму, что и узел, вставленный в DOM.

Теоретически вместо этого следует использовать requestAnimationFrame, что как раз и предназначено для таких ситуаций.

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

function move(color){
  let clone=document.getElementById("test").cloneNode(true);
  clone.id=color;
  clone.style.display="block";
  clone.style.backgroundColor=color;
  document.getElementById("main").prepend(clone);
  requestAnimationFrame(() => {
    requestAnimationFrame(() => {
        clone.style.left="500px";
    })
  })
}

вот фрагмент: я нахожу то же самое во фрагменте SO

function move(color) {
  let clone = document.getElementById("test").cloneNode(true);
  clone.id = color;
  clone.style.display = "block";
  clone.style.backgroundColor = color;
  document.getElementById("main").prepend(clone);
  requestAnimationFrame(() => {
    requestAnimationFrame(() => {
      clone.style.left = "500px";
    })
  })
}


setTimeout(() => move("red"), 500);
setTimeout(() => move("green"), 750);
#main {
  display: block;
  width: 100vw;
  height: 100vh;
  background-color: blue;
}

.test {
  position: absolute;
  display: none;
  width: 100px;
  height: 100px;
  background-color: red;
  transition: left 1s ease;
  transform: scale(1);
  left: 0px;
}
<div id="main"></div>
<div id="test" class="test"></div>

person Sheraff    schedule 25.10.2020
comment
requestAnimationFrame срабатывает перед следующей отрисовкой, так что здесь это не поможет, если между ними ничего не вызвало перекомпоновку. Вы можете дождаться тайм-аута 0 в обратном вызове raf, но не нужно ничего ждать, как объяснено в связанном обмане. - person Kaiido; 26.10.2020
comment
@Kaiido ссылка не объясняет это IMO, так как здесь .prepend уже должен запускать пересчет (во время текущего кадра), а RAF не должен выполняться в текущем кадре. Что мне не хватает? - person Sheraff; 26.10.2020
comment
после связанного с вами потока, вот полное объяснение, которое использует один RAF для запуска анимации после удаления display: nonestackoverflow.com/questions/54344996/ - person Sheraff; 26.10.2020
comment
Да, это объясняет это, признаю, по нескольким ссылкам. .prepend не должен вызывать перекомпоновку нет, продолжайте следовать ссылкам до этого ответа чтобы лучше понять, что такое перекомпоновка, и как было бы ужасно, если бы каждая манипуляция с DOM, например добавление в начале, вызывала ее. Кроме того, по предоставленной вами ссылке я сказал (да, я написал все эти ответы), что вам даже не нужен rAF, вы можете делать все это синхронно, как показано в помеченном как дубликат. jsfiddle.net/j9dotnyc - person Kaiido; 26.10.2020