Как остановить анимацию ключевого кадра ровно через 1 секунду, не используя setTimeout? - проблемные события в очереди - PullRequest
0 голосов
/ 07 сентября 2018

Я думаю, что эту проблему будет действительно трудно решить ...

Я создал спидометр, который показывает количество землетрясений, произошедших в моем городе. Я хочу оживить этот спидометр двумя способами: background-color (зеленый, когда нет землетрясений и красный, когда есть 3000 землетрясения) и ширина этого цветного div (div, где я анимирую background-color). Таким образом, ширина будет 0 , когда нет землетрясений, и будет 100%, когда есть 3000 землетрясений.

Анимация составляет 2 секунды, например, если у меня 1500 землетрясения: Добавить класс для анимации спидометра

  $('#first').addClass('first-start');

И с помощью setTimeout я добавляю класс, чтобы остановить анимацию через 1 секунду

 setTimeout(function() {
   $('#first').addClass('first-pause');
 }, 1000);

Этот код почти всегда отлично работает.

Теперь я добавлю фрагмент:

$('#first').addClass('first-start');

setTimeout(function() {
  $('#first').addClass('first-pause');
}, 1000);
#page {
  margin-top: 50px;
  width: 300px;
  height: 300px;
  background-color: #000;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  z-index: 4;
  overflow: hidden;
}

#box-first{
  width: 200px;
  height: 100px;
  background-color: #fff;
  border-radius: 200px 200px 0 0;
  margin-top: 10px;
  margin-bottom: 10px;
  position: relative;
  display: flex;
  justify-content: flex-end;
  align-items: flex-start;
  z-index: 3;
  overflow: hidden;
}

#first{
  border-radius: 200px 200px 0 0;
  margin: 0;
  background: red;
  width: 200px;
  height: 100px;
  transform: rotate(180deg);
  transform-origin: 50% 100%;
  position: absolute;
  top: 0px;
  right: 0px;
  border: 0;
  z-index: 1;
}

#n1{
  font-size: 20px;
  color: #fff;
  font-weight: bold;
  position: absolute;
  left: 50px;
  right: 0;
  text-align: center;
  top: 50px;
  bottom: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  width: 100px;
  height: 50px;
  background: #000;
  border-radius: 100px 100px 0 0;
  z-Index: 1;
  overflow: hidden;
}

@keyframes first {
  0% {
    background-color: green;
    transform: rotate(180deg);
  }
  33% {
    background-color: yellow;
    transform: rotate(240deg);
  }
  66% {
    background-color: orange;
    transform: rotate(300deg);
  }
  100% {
    background-color: red;
    transform: rotate(360deg);
  }
}



.first-start {
  animation: first 2s linear;
}

.first-pause {
  animation-play-state: paused;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="page">
  <div id="box-first">
    <div id="first">

    </div>
    <div id="n1">
      1500
    </div>
  </div> 
</div>

https://jsfiddle.net/hoymds97/

Проблема в том, что я использую этот код в большом файле (4000 строк) с большим количеством событий и в той же функции есть 8 спидометров. Я заметил, что иногда (когда есть больше событий) setTimeout не запускается сразу после добавления класса для анимации спидометра.

В результате анимация остановится после ... В нашем случае, например, он блокируется через 1700 миллисекунд, а не 1000 секунд. Иногда он останавливается даже через 2 секунды.

Я думаю, что проблема во многих событиях в queue.

Так, как я могу решить эту проблему?

Можно решить, используя всегда setTimeout или без него?

Надеюсь, вы мне поможете и извините за мой английский.

1 Ответ

0 голосов
/ 08 сентября 2018

Вот совершенно новая идея, основанная на переходе, а не на анимации, в которой вы можете легко настроить состояние без проблем с синхронизацией.

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

Вот простой код для иллюстрации окраски:

.box {
  background: linear-gradient(to right, green, yellow, orange, red);
  background-size: 2000% 100%;
  transition:1s;
  background-repeat: no-repeat;
  background-position: 0 0;
  height: 200px;
}

.box:hover {
  background-position: 100% 0;
}
<div class="box">

</div>

Как видите, я определил градиент с помощью 4 цветов, и нам просто нужно настроить background-size, чтобы получить окраску (0% для зеленого и 100% для красного). Это не будет точно визуально, потому что у нас не будет сплошного цвета, как с анимацией, и по этой причине я сделал background-size достаточно большим, чтобы создать иллюзию сплошного цвета.

Теперь нам просто нужно найти значения background-position и degree, что довольно просто. backround-position - это значение от 0% до 100%, а степень - от 180deg до 360deg. Для состояния 50% мы будем логически использовать 50% для фона-положения и 270deg для преобразования, а для состояния x% мы будем использовать соответственно x% и x%*(360deg - 180deg) + 180deg = x%*180deg + 180deg = 180deg(x% + 1)

Вот пример с 50% (наведите курсор, чтобы увидеть)

#page {
  margin-top: 50px;
  width: 300px;
  height: 300px;
  background-color: #000;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  z-index: 4;
  overflow: hidden;
}

#box-first{
  width: 200px;
  height: 100px;
  background-color: #fff;
  border-radius: 200px 200px 0 0;
  margin-top: 10px;
  margin-bottom: 10px;
  position: relative;
  display: flex;
  justify-content: flex-end;
  align-items: flex-start;
  z-index: 3;
  overflow: hidden;
}

#first{
  border-radius: 200px 200px 0 0;
  margin: 0;
  background: linear-gradient(to right, green, yellow, orange, red);
  background-size: 2000% 100%;
  background-repeat:no-repeat;
  background-position:0% 0%;
  transition:1s;
  width: 200px;
  height: 100px;
  transform: rotate(180deg);
  transform-origin: 50% 100%;
  position: absolute;
  top: 0px;
  right: 0px;
  border: 0;
  z-index: 1;
}
#box-first:hover #first{
  transform: rotate(270deg);
  background-position:50% 0%;
}

#n1{
  font-size: 20px;
  color: #fff;
  font-weight: bold;
  position: absolute;
  left: 50px;
  right: 0;
  text-align: center;
  top: 50px;
  bottom: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  width: 100px;
  height: 50px;
  background: #000;
  border-radius: 100px 100px 0 0;
  z-Index: 1;
  overflow: hidden;
}
<div id="page">
  <div id="box-first">
    <div id="first">

    </div>
    <div id="n1">
      1500
    </div>
  </div> 
</div>

Чтобы сделать это динамичным, нам нужно отрегулировать значения с помощью JS, и переход сделает всю работу. Для этого мы можем определить data-attribute для состояния, которое мы конвертируем в необходимое значение.

Вот пример, где я также упростил html и использовал псевдоэлемент и переменные CSS

setTimeout(function() {
  $('.box').each(function() {
    var d = $(this).data('state');
    $(this).attr("style", "--s:" + d);
  });
}, 1000);
body {
  margin: 0;
  background: #000;
}

.box {
  width: 200px;
  height: 100px;
  background-color: #fff;
  border-radius: 200px 200px 0 0;
  margin: 10px;
  position: relative;
  display: inline-flex;
  z-index: 0;
  overflow: hidden;
}

.box:before {
  content: "";
  position: absolute;
  z-index: -1;
  border-radius: 200px 200px 0 0;
  background: linear-gradient(to right, green, yellow, orange, red);
  background-size: 2000% 100%;
  background-repeat: no-repeat;
  background-position: calc(var(--s, 0) * 1%) 0%;
  transition:2s linear;
  width: 200px;
  height: 100px;
  transform: rotate(calc((var(--s, 0)/100 + 1)*180deg));
  transform-origin: 50% 100%;
  top: 0px;
  right: 0px;
}

.box:after {
  content: attr(data-number);
  font-size: 20px;
  color: #fff;
  font-weight: bold;
  text-align: center;
  margin: auto auto 0;
  width: 100px;
  height: 50px;
  line-height: 50px;
  background: #000;
  border-radius: 100px 100px 0 0;
  z-Index: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="box" data-number="1500" data-state="50"></div>

<div class="box" data-number="1000" data-state="20"></div>

<div class="box" data-number="3000" data-state="80"></div>

<div class="box" data-number="6000" data-state="100"></div>

Вы можете заметить, что у всех будет одинаковая продолжительность, так как переход одинаков для всех. Если вы хотите использовать другую продолжительность и сохранить ту же скорость, просто используйте переменную CSS внутри перехода.

setTimeout(function() {
  $('.box').each(function() {
    var d = $(this).data('state');
    $(this).attr("style", "--s:" + d);
  });
}, 1000);
body {
  margin: 0;
  background: #000;
}

.box {
  width: 200px;
  height: 100px;
  background-color: #fff;
  border-radius: 200px 200px 0 0;
  margin: 10px;
  position: relative;
  display: inline-flex;
  z-index: 0;
  overflow: hidden;
}

.box:before {
  content: "";
  position: absolute;
  z-index: -1;
  border-radius: 200px 200px 0 0;
  background: linear-gradient(to right, green, yellow, orange, red);
  background-size: 2000% 100%;
  background-repeat: no-repeat;
  background-position: calc(var(--s, 0) * 1%) 0%;
  transition: calc(2s * var(--s, 0)/100) linear;
  width: 200px;
  height: 100px;
  transform: rotate(calc((var(--s, 0)/100 + 1)*180deg));
  transform-origin: 50% 100%;
  top: 0px;
  right: 0px;
}

.box:after {
  content: attr(data-number);
  font-size: 20px;
  color: #fff;
  font-weight: bold;
  text-align: center;
  margin: auto auto 0;
  width: 100px;
  height: 50px;
  line-height: 50px;
  background: #000;
  border-radius: 100px 100px 0 0;
  z-Index: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="box" data-number="1500" data-state="50"></div>

<div class="box" data-number="1000" data-state="20"></div>

<div class="box" data-number="3000" data-state="80"></div>

<div class="box" data-number="6000" data-state="100"></div>
...