Get/set current @keyframes percentage/change keyframes

試著忘記壹切 提交于 2019-12-17 11:51:22

问题


Is it possible to get/set the current animation percentage of a CSS3 @keyframes animation using javascript, jQuery, or some other means?

Say for example there is a div with class called .spin that simply spins around in a circle using a keyframe also called spin.

  1. I have tried to get the current percentage value of the animation using $('.spin').css('animation'), $('.spin').css('animation: spin'), and a couple other ways, but they all alert empty

  2. I'm also interested in changing the original animation at each predefined keyframe % and I have tried things like $('.spin').css('animation', '..new definition here...') and $('.spin').css('spin', '50%', '...new definition here...) to no avail.

Any ideas?


回答1:


I achieved roughly what I wanted using pure javascript with my CSS3.

For my experiment to come up with a way to do these objectives, I created a basic CSS3 animation rotating a circle around a small circular path. My goal was to, when a button was clicked, change the origin of the animation to the new location

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 = 0;
  }
  result.innerHTML = currentPercent;
}, 40);

Note on this solution:

  • It is not perfect due because the counter keeps running when another tab is clicked but the animation stops, so they become un-synchronized
  • It is also faulty when the button is clicked long after the last click. Evidently the setInterval runs a little bit longer than the animation, so they become less and less synced each iteration
  • I looked everywhere for a more perfect solution but have been unable to come up with one as of yet

To achieve the second goal of setting a new definition for an animation's % value it took a bit of a more complex solution. After pouring through dozens of articles and web pages (the important ones listed below), I managed to come up with the following solution:

var circle = document.getElementById('circle'), 
    button = document.getElementById('button');
var result = document.getElementById('result'), 
    totalCurrentPercent = 0,
    currentPercent = 0;
var showPercent = window.setInterval(function() {
  if(currentPercent < 100)
  {
    currentPercent += 1;
  }
  else {
    currentPercent = 0;
  }
  result.innerHTML = currentPercent;
}, 40);
function findKeyframesRule(rule) {
    var ss = document.styleSheets;
    for (var i = 0; i < ss.length; ++i) {
        for (var j = 0; j < ss[i].cssRules.length; ++j) {
            if (ss[i].cssRules[j].type == window.CSSRule.WEBKIT_KEYFRAMES_RULE && ss[i].cssRules[j].name == rule) { return ss[i].cssRules[j]; }
        }
    }
    return null;
}
function change(anim) {
  var keyframes = findKeyframesRule(anim),
      length = keyframes.cssRules.length;
  var keyframeString = [];  
  for(var i = 0; i < length; i ++)
  {
    keyframeString.push(keyframes[i].keyText);
  }
  var keys = keyframeString.map(function(str) {
    return str.replace('%', '');
  });
  totalCurrentPercent += currentPercent;
  if(totalCurrentPercent > 100)
  {
    totalCurrentPercent -= 100;
  }
  var closest = getClosest(keys);  
  var position = keys.indexOf(closest), 
      firstPercent = keys[position];
  for(var i = 0, j = keyframeString.length; i < j; i ++)
  {
    keyframes.deleteRule(keyframeString[i]);
  }
  var multiplier = firstPercent * 3.6;
  keyframes.insertRule("0% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 0) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 0) + "deg); background-color:red; }");
  keyframes.insertRule("13% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 45) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 45) + "deg); }");
  keyframes.insertRule("25% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 90) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 90) + "deg); }");
  keyframes.insertRule("38% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 135) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 135) + "deg); }");
  keyframes.insertRule("50% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 180) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 180) + "deg); }");
  keyframes.insertRule("63% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 225) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 225) + "deg); }");
  keyframes.insertRule("75% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 270) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 270) + "deg); }");
  keyframes.insertRule("88% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 315) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 315) + "deg); }");
  keyframes.insertRule("100% { -webkit-transform: translate(100px,100px) rotate(" + (multiplier + 360) + "deg) translate(-100px,-100px) rotate(" + (multiplier + 360) + "deg); }");
  circle.style.display = "inherit";
  circle.style.webkitAnimationName = anim; 
  window.clearInterval(showPercent);
  currentPercent = 0;
  showPercent = self.setInterval(function() {
    if(currentPercent < 100)
    {
      currentPercent += 1;
    }
    else {
      currentPercent = 0;
    }
    result.innerHTML = currentPercent;
  }, 40); 
}
button.onclick = function() {
    circle.style.webkitAnimationName = "none";
    circle.style.display = "none";
    setTimeout(function () { 
        change("rotate"); 
    }, 0);
}
function getClosest(keyframe) {
  var curr = keyframe[0];
  var diff = Math.abs (totalCurrentPercent - curr);
  for (var val = 0; val < keyframe.length; val++) {
    var newdiff = Math.abs(totalCurrentPercent - keyframe[val]);
    if (newdiff < diff) {
      diff = newdiff;
      curr = keyframe[val];
     }
  }
  return curr;
}

Check out the experiment itself here including comments describing what each part of the javascript is doing

Notes on this solution:

  • I tried to make the change function as non-hard-coded as possible
  • It works alright for approximating the current @keyvalue percentage
  • The transition from one animation to the other is only as smooth as however close the nearest % value of the animation is to the current % of the animation, so adding more % definitions to the animation would make it even more smooth

In the process of trying to come up with a solution for the problem, I found these useful resources:

  • RussellUresti's answer in this SO post and corresponding example was quite influential and greatly aided my final solution
  • To get the closest value based on an input and array values, I used paxdiablo's method in this SO post (thank you)
  • This plugin, while I didn't use it myself, seemed to achieve a very similar (though seemingly not quite as customizable) effect in jQuery

---EDIT---

If you are just using CSS transitions or you can change your animation to just use transitions instead, you can use this simpler approach. You can pause the transition by copying the attributes changed by the transition, setting the attributes to those changed attributes, and then removing the class that animates it. Of course if you are using the same object to animate/pause you will need to animate the first click then pause it the next click. You can easily do the pure javascript equivalent as well

Note: the !important in the CSS changed attribute is necessary unless you have a more leveled selector for the animation class than the jQuery selector, aka something like div #defaultID.animationClass { as opposed to just #defaultID.animationClass {. Since #defaultID and #defaultID.animationClass are both one level, this example requires the !important

--Another Edit--

For more information on this topic, check out my post on CSS-Tricks



来源:https://stackoverflow.com/questions/18006099/get-set-current-keyframes-percentage-change-keyframes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!