Преобразование времени в проценты анимации по ключевым кадрам

Я работаю над анимацией и пытаюсь понять, как в нее вписываются ключевые кадры.

Я хочу показать элемент в течение 3 секунд, исчезнуть в течение 1 секунды, подождать 3 секунды, исчезнуть в течение 1 секунды, быть видимым в течение 3 секунд. Итого 8 секунд (3 + 1 + 3 + 1).

Я не знаю, как записать это как ключевые кадры. У меня есть альтернативный набор в моей анимации, поскольку он использует проценты. Вот что у меня есть на данный момент:

time = 0;
window.addEventListener("load", function() {
   setInterval(function() {
    var label = document.getElementById("label");
    
    if (time==0) {
      label.innerHTML = ++time;
    }
    else {
      label.innerHTML = ++time;
    }
    if (time>=8) time = 0;
    
   }, 1000);
})
* {
		margin: 0;
		padding: 0;
    font-family:sans-serif;
	}
	#Icons_A0 {
		position: absolute;
		box-sizing: border-box;
		transform: translateX(-50%) translateY(-50%);
		left: 50%;
		top: 50%;
		border: 1px solid #A1A1A1;
		background: #E5E5E5;
		width: 234px;
		height: 238px;
		background-color: rgba(255,255,255,1);
		overflow: hidden;
		opacity: 1;
	}
	#Rectangle_175 {
		opacity: 1;
		fill: rgba(75,134,193,1);
		stroke: rgb(84, 75, 193);
		stroke-width: 4px;
		stroke-linejoin: miter;
		stroke-linecap: butt;
		stroke-miterlimit: 4;
		shape-rendering: auto;
	}
	.Rectangle_175 {
		position: absolute;
		overflow: visible;
		width: 134.35028076171875px;
		height: 134.3502655029297px;
		left: 49.825px;
		top: 76.825px;
		transform: rotate(45deg);
		transform-origin: left;
	}
	#Ellipse_49 {
		opacity: 1;
		fill: rgba(180,180,180,1);
		stroke: rgb(112, 112, 112);
		stroke-width: 1px;
		stroke-linejoin: miter;
		stroke-linecap: butt;
		stroke-miterlimit: 4;
		shape-rendering: auto;
	}
	.Ellipse_49 {
		position: absolute;
		overflow: visible;
		width: 56px;
		height: 56px;
		left: 72px;
		top: 51px;
    animation: fadein 8s linear 0s infinite alternate;
	}
	@keyframes fadein {
	
		0% {
			opacity: 1;
		}
		20% {
			opacity: 1;
		}
		30% {
			opacity: 0;
		}
		60% {
			opacity: 0;
		}
		70% {
			opacity: 1;
		}
		100% {
			opacity: 1;
		}
	
	}
<div id = "Icons_A0">
	<svg data-name = "Rectangle 175" data-type = "Rectangle" class = "Rectangle_175">
		<rect id = "Rectangle_175" rx = "0" ry = "0" x = "0" y = "0" width = "120" height = "70">
		</rect>
	</svg>
	<svg class = "Ellipse_49">
		<ellipse id = "Ellipse_49" rx = "28" ry = "28" cx = "28" cy = "28">
		</ellipse>
	</svg>
  <span id = "label"></span>
</div>
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
3
0
47
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Если ваша общая длина составляет 8 секунд, и вам просто нужно перевести это в проценты, математика будет довольно простой: 100/8 = 12,5

Итак, ваши ключевые кадры войдут в:

1 sec : 12.5 * 1 = 12.5%
4 sec : 12.5 * 4 = 50%
7 sec : 12.5 * 7 = 87.5%
8 sec : 12.5 * 8 = 100%
Ответ принят как подходящий

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

@keyframes fadein {
  0% {
    opacity: 1;

  }
  37.5% {
    /* 3 / 8 */
    opacity: 1; 
  }
  50% {
    /* (3 + 1) / 8 */
    opacity: 0.0; 
  }
  87.5% {
    /* (3 + 1 + 3) / 8 */
    opacity: 0.0; 
  }
  100% {
    opacity: 1; 
  }
}

В комментариях показано, как рассчитываются проценты для различных ключевых кадров в соответствии с вашими требованиями. Другое ключевое изменение, которое необходимо сделать, - удалить поведение alternate из правила анимации, чтобы цикл анимации повторялся согласованно, как требуется:

/* remove alternate */
animation: fadein 8s linear 0s infinite;

Вот урезанная копия вашего кода, чтобы изолировать анимированный круг:

function animationListener(event) {
  var type = event.type;
  var label = type;
  
  if (type= = "animationiteration") {
    if (app.interval!=null) {
      clearInterval(app.interval);
    }
    app.time = 0;
    app.startTime = new Date().getTime();
    app.interval = setInterval(intervalFunction, 1000);
    intervalFunction();
    label = "iteration";
  }
  else if (type= = "animationstart") {
    label = "start";
  }
  else if (type= = "animationend") {
    label = "end";
  }
  
  app.stateLabel.innerHTML = label;
}

function intervalFunction() {
  var time = new Date().getTime();
  app.timeLabel.innerHTML = Math.round((time - app.startTime)/1000);
  app.keyframeLabel.innerHTML = window.getComputedStyle(app.ellipse).content;
}

function loadHandler() {
  app.ellipse = document.getElementById("Ellipse_49").parentNode;
  app.stateLabel = document.getElementById("stateLabel");
  app.timeLabel = document.getElementById("timeLabel");
  app.keyframeLabel = document.getElementById("keyframeLabel");
  
  app.ellipse.addEventListener("animationiteration", animationListener);
  app.ellipse.addEventListener("animationend", animationListener);
  app.ellipse.addEventListener("animationstart", animationListener);
}

document.addEventListener("DOMContentLoaded", loadHandler);
var app = {};
* {
  font-family: sans-serif;
  font-size: 11px;
  letter-spacing: .6px;
}

#Ellipse_49 {
  opacity: 1;
  fill: rgba(180, 180, 180, 1);
  stroke: rgb(112, 112, 112);
  stroke-width: 1px;
  stroke-linejoin: miter;
  stroke-linecap: butt;
  stroke-miterlimit: 4;
  shape-rendering: auto;
}

.Ellipse_49 {
  position: absolute;
  overflow: visible;
  width: 56px;
  height: 56px;
  left: 72px;
  top: 51px;
  
  /* remove alternate */
  animation: fadein 8s linear 0s infinite;
}

#container {
   top: 130px;
   left: 10px;
   position: relative;
   display: block;
   align-items: center;
   
}

label {
  width: 80px;
  display: inline-block;
}

@keyframes fadein {
  0% {
    opacity: 1;
    content: "show";
  }
  37.5% {
    /* 3 / 8 */
    opacity: 1;
    content: "fade out";
  }
  50% {
    /* (3 + 1) / 8 */
    opacity: 0.0;
    content: "wait";
  }
  87.5% {
    /* (3 + 1 + 3) / 8 */
    opacity: 0.0;
    content: "fade in";
  }
  100% {
    opacity: 1;
    content: "show";
  }
}
<svg class = "Ellipse_49">
		<ellipse id = "Ellipse_49" rx = "28" ry = "28" cx = "28" cy = "28">
		</ellipse>
</svg>


<div id = "container">
  <label>time: </label>
  <span id = "timeLabel"></span>
  <br>
  <label>state: </label>
  <span id = "stateLabel"></span>
  <br>
  <label>key frame: </label>
  <span id = "keyframeLabel"></span>
</div>

Я добавил в ваш пример состояние анимации, время воспроизведения анимации и информацию о ключевых кадрах анимации. Кажется, что время совпадает с событиями, но вычисленный ключевой кадр анимации, кажется, указывает на предстоящее событие, которое должно произойти через 1 секунду. Кроме того, он запускается после первой итерации. Примечание. Я нашел здесь пример использования содержимого для хранения заметок по ключевым кадрам jsfiddle.net/z4ytsesh. Не стесняйтесь обновлять его.

1.21 gigawatts 16.01.2019 18:42

Добавление здесь ответа @DacreDenny, чтобы я мог изменить его и добавить примечания.

function animationListener(event) {
  var type = event.type;
  var label = type;
  
  if (type= = "animationiteration") {
    if (app.interval!=null) {
      clearInterval(app.interval);
    }
    app.time = 0;
    app.startTime = new Date().getTime();
    app.interval = setInterval(intervalFunction, 15);
    intervalFunction();
    label = "iteration";
  }
  else if (type= = "animationstart") {
    label = "start";
  }
  else if (type= = "animationend") {
    label = "end";
  }
  
  app.stateLabel.innerHTML = label;
}

function intervalFunction() {
  var currentTime = new Date().getTime();
  var time = (currentTime - app.startTime)/1000;
  var duration = parseFloat(window.getComputedStyle(app.ellipse).animationDuration);
  var maxValue = 100;
  var position = ((time * maxValue)/duration);
  
  app.timeLabel.innerHTML = Math.round(time);
  app.keyframeLabel.innerHTML = window.getComputedStyle(app.ellipse).content;
  app.timelineRange.value = position;
  app.positionLabel.innerHTML = Math.round(position) + "%";
}

function loadHandler() {
  app.ellipse = document.getElementById("Ellipse_49").parentNode;
  app.stateLabel = document.getElementById("stateLabel");
  app.timeLabel = document.getElementById("timeLabel");
  app.keyframeLabel = document.getElementById("keyframeLabel");
  app.timelineRange = document.getElementById("timelineRange");
  app.positionLabel = document.getElementById("positionLabel");
  
  app.ellipse.addEventListener("animationiteration", animationListener);
  app.ellipse.addEventListener("animationend", animationListener);
  app.ellipse.addEventListener("animationstart", animationListener);
}

document.addEventListener("DOMContentLoaded", loadHandler);
var app = {};
* {
  font-family: sans-serif;
  font-size: 11px;
  letter-spacing: .6px;
}

@keyframes fadein {
  0% {
    opacity: 1;
    content: "show";
  }
  37.5% {
    /* 3 / 8 */
    opacity: 1;
    content: "fade out";
  }
  50% {
    /* (3 + 1) / 8 */
    opacity: 0.0;
    content: "wait";
  }
  87.5% {
    /* (3 + 1 + 3) / 8 */
    opacity: 0.0;
    content: "fade in";
  }
  100% {
    opacity: 1;
    content: "show";
  }
}

#Ellipse_49 {
  opacity: 1;
  fill: rgba(180, 180, 180, 1);
  stroke: rgb(112, 112, 112);
  stroke-width: 1px;
  stroke-linejoin: miter;
  stroke-linecap: butt;
  stroke-miterlimit: 4;
  shape-rendering: auto;
}

.Ellipse_49 {
  position: absolute;
  overflow: visible;
  width: 50px;
  height: 50px;
  left: 20px;
  top: 50px;
  
  /* remove alternate */
  animation: fadein 4s linear 0s infinite;
}

#container {
   top: 130px;
   left: 10px;
   position: relative;
   display: block;
}

label {
  width: 80px;
  display: inline-block;
}

input[type=range] {
  outline: 0px solid red;
  display: block;
  width: 90%;
  margin-left: 0;
}
input[type=range]::-webkit-slider-runnable-track {
  width: 100%;
  height: 8.4px;
  cursor: pointer;
  box-shadow: 0px 0px 0px #000000, 0px 0px 0px #0d0d0d;
  background: rgb(255,255,255);
}
<svg class = "Ellipse_49">
		<ellipse id = "Ellipse_49" rx = "28" ry = "28" cx = "28" cy = "28">
		</ellipse>
</svg>


<div id = "container">
  <input id = "timelineRange" type = "range" value = "0" min = "0" max = "100">
  <br>
  
  <label>time: </label>
  <span id = "timeLabel"></span>
  <br>
  <label>position: </label>
  <span id = "positionLabel"></span>
  <br>
  <label>state: </label>
  <span id = "stateLabel"></span>
  <br>
  <label>key frame: </label>
  <span id = "keyframeLabel"></span>
  <br>
  <br>
</div>

Другие вопросы по теме