将时间转换为关键帧动画百分比

3
我正在制作一段动画,想了解关键帧在其中的作用。
我希望展示一个元素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>

3个回答

2
如果我理解您的问题正确,那么可以通过以下关键帧集来实现:
@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

1
如果您的总长度为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%

0

在这里添加 @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>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接