Translation of time to keyframe animation percentages

css keyframe animation generator
keyframe animation examples
css keyframe animation examples
css animation
css animation-delay
css animation shorthand
animation-fill-mode
css animation loop

I'm working on an animation and I'm trying to understand how keyframes fit into it.

I want to show an element for 3 seconds, fade out for 1 second, wait for 3 seconds, fade in for 1 second, be visible for 3 seconds. So a total of 8 seconds (3 + 1 + 3 + 1).

I don't know how to write that as keyframes. I have alternate set in my animation since it's using percentages. Here is what I have so far:

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>

If I understand your question correctly, then this can be achieved via the following key frame set:

@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; 
  }
}

The comments show how the percentages for different key frames are calculated based on your requirements. The other key change to make is to remove the alternate behaviour from your animation rule, to ensure the animation cycle repeats in a consistent fashion as required:

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

Here's a stripped down copy of your code to isolate the animated circle:

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>

CSS Keyframe Animation with Pause between Keyframes, When animating objects with CSS The key frame values 0% and 100% can be replaced with the keywords ____? Developers frequently write keyframes as percentages, which differs from a keyframe that is referenced by a frame number or timeline. Translate keyframes into percentages so that the entire animation can be scaled in time to be slower or faster.

If your total length is 8s, and you just need to translate that to percentages the math is pretty simple: 100/8=12.5

So, your keyframes would come in at:

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%

An Introduction To CSS Keyframes Animation, If you use steps(10) in your animation, it will make sure only 10 keyframes happen in the allotted time. .move { animation: move 10s steps(10)  To achieve the first goal of getting the percentage value of the animation I simply approximated the current percentage using the following setInterval, which displays the approximated current percent. This runs every 40 milliseconds to correspond with the duration of the animation (milliseconds / 100) var result = document.getElementById('result'), currentPercent = 0; var showPercent = window.setInterval(function() { if(currentPercent < 100) { currentPercent += 1; } else { currentPercent

Adding @DacreDenny's answer here so I can modify it and add notes.

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>

@keyframes, So when you call the keyframe animation, you use the total seconds: 1 second, you'll need to have the changes happen over 1/5 of the time, or 20%. 0); } /* Finish changes by here */ 20% { transform: translate(200px, 0); }  1 Translation of time to keyframe animation percentages Jan 16 '19. 1 Image sizes are incorrect when page is rendered in MS Edge Jul 17 '19. 1 Typography & wordpress

Keyframe Animation Syntax, The @keyframes CSS at-rule is used to define the behavior of one cycle of a '​100%' keyframe selectors define the waypoints, or percentage points in time, at the end of the animation, translated downwards and faded out. So to make our animation run for only 1 second, you’ll need to have the changes happen over 1/5 of the time, or 20%. Verbously, that would look like this: @keyframes move { 0% { transform: translate(0, 0); } /* Finish changes by here */ 20% { transform: translate(200px, 0); } /* Between 20% and 100%, nothing changes */ 100% { transform: translate(200px, 0); } }

CSS Keyframe Animation with Delay Between Iterations, If a given animation time offset is duplicated, all keyframes in the @keyframes rule for that percentage are used for that frame. There is  A percentage value specifies that the key frame ends at some percentage of the animation's Duration. In XAML, you specify the percentage as a number followed by the % symbol. In code, you use the FromPercent method and pass it a Double indicating the percentage. The value must be greater than or equal to 0 and less than or equal to 100 percent.

@keyframes, Calculate the percentage (%) of the animation keyframes. animation time (%) = (​animation time / total time) * 100 animation pause  KFM_DATA followed by a bitwise combination of: KFM_TRANSLATION and KFM_ROTATION. By default both rotations and translations must be provided. If you specify one or the other, you should only include translations or rotations in your keyframe list. Must be specified at the time the keyframe list is provided.

Comments
  • I've added animation state, animation play time and animation keyframe info to your example. The time seems to coincide with the events but the computed animation key frame seems to indicate an upcoming event to occur in 1 second. Also, it starts after the first iteration. Note: I found an example of using the content to store keyframe notes here jsfiddle.net/z4ytsesh. Feel free to update it.