【问题标题】:Get/set current @keyframes percentage/change keyframes获取/设置当前@keyframes 百分比/更改关键帧
【发布时间】:2013-08-03 02:05:13
【问题描述】:

是否可以使用 javascript、jQuery 或其他方式获取/设置 CSS3 @keyframes 动画的当前动画百分比?

例如,有一个div,其类名为.spin,它使用一个也称为spin 的关键帧简单地绕圈旋转。

  1. 我尝试使用$('.spin').css('animation')$('.spin').css('animation: spin') 和其他几种方式获取动画的当前百分比值,但它们都警告为空

  2. 我也有兴趣在每个预定义的关键帧 % 处更改原始动画,我尝试了 $('.spin').css('animation', '..new definition here...')$('.spin').css('spin', '50%', '...new definition here...) 之类的方法,但无济于事。

有什么想法吗?

【问题讨论】:

    标签: javascript css css-transitions css-animations


    【解决方案1】:

    我使用纯 JavaScript 和我的 CSS3 大致实现了我想要的。

    为了让我的实验想出一种方法来实现这些目标,我创建了一个基本的 CSS3 动画,围绕一个小圆形路径旋转一个圆圈。我的目标是,当单击按钮时,将动画的原点更改为新位置

    为了实现获得动画百分比值的第一个目标,我只是使用以下setInterval 来近似当前百分比,它显示了近似的当前百分比。这每 40 毫秒运行一次,以对应动画的持续时间(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);
    

    此解决方案的注意事项:

    • 这并不完美,因为当单击另一个选项卡时计数器会继续运行但动画停止,因此它们变得不同步
    • 在最后一次单击后长时间单击按钮也是错误的。显然 setInterval 的运行时间比动画长一点,所以它们在每次迭代中变得越来越不同步
    • 我到处寻找更完美的解决方案,但至今仍未找到一个

    要实现第二个目标,即为动画的 % 值设置新定义,需要一些更复杂的解决方案。在翻阅了几十篇文章和网页(下面列出了重要的)之后,我设法想出了以下解决方案:

    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 包括描述 javascript 的每个部分在做什么的 cmets

    关于此解决方案的说明:

    • 我试图使更改函数尽可能非硬编码
    • 它可以很好地逼近当前的@keyvalue 百分比
    • 从一种动画到另一种动画的过渡只有在动画的最接近 % 值与动画的当前 % 的接近程度时才会如此平滑,因此向动画添加更多 % 定义会使其更加平滑李>

    在尝试解决问题的过程中,我发现了这些有用的资源:

    ---编辑---

    如果您只使用 CSS 过渡,或者您可以将动画更改为只使用过渡,您可以使用 this simpler approach。您可以通过复制由转换更改的属性,将属性设置为那些更改的属性,然后删除为其设置动画的类来暂停转换。当然,如果您使用相同的对象进行动画/暂停,则需要为第一次单击设置动画,然后在下次单击时暂停。你也可以轻松地做纯 javascript 等价物

    注意: CSS changed 属性中的 !important 是必需的,除非您有比 jQuery 选择器更层次的动画类选择器,也就是 div #defaultID.animationClass { 而不是 @ 987654335@。由于#defaultID#defaultID.animationClass 都是一层,所以这个例子需要!important

    --另一个编辑--

    有关此主题的更多信息,请查看my post on CSS-Tricks

    【讨论】:

      【解决方案2】:

      参加派对永远不会太晚。

      //Animation pause at specific percentage, Example: pauseAnimationAt("myDiv",33,10)
          function pauseAnimationAt(l,p,d){
            percentage = p/d*1000;
            function pauseAnimation(){
              setTimeout(function(){ document.getElementById(l).style="animation-play-state: paused"; }, percentage);//l = Element ID, p = percentage when you wanna stopt animation, d = Duration of animation (Yes, you need to know that)
            }
            document.getElementById(l).addEventListener("animationstart", pauseAnimation());
          }
      

      【讨论】:

      • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
      • 这种方法非常不精确,这是帖子中概述的问题和接受的答案的一部分。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-13
      • 2020-08-15
      • 2017-12-27
      相关资源
      最近更新 更多