【问题标题】:Manually stepping through CSS animation with JavaScript使用 JavaScript 手动单步执行 CSS 动画
【发布时间】:2013-06-17 17:09:33
【问题描述】:

如果我有这样的 CSS 关键帧动画

@keyframes flash-red {
  50% {
    background: #f00;
  }
}

#goflash.anm-flash {
  animation-name: flash-red;
  animation-duration: .5s;
  background: rgba(255, 0, 0, 0);
}

然后我总是可以像这样触发动画:

var gf = document.querySelector("#goflash");
gf.classList.remove("anm-flash");
setTimeout(function() {
  gf.classList.add("anm-flash");
}, 50);

有没有办法覆盖动画持续时间/动画定时功能以依赖于 JavaScript?我希望能够说出类似gf.animate("flash-red", "50%") 来使gf 的背景变红,或者说gf.animate("flash-red", "75%") 使背景更像rgba(255, 0, 0, .5)

理想情况下,相同的技术也适用于过渡。 gf.transitionTo("new-class", "50%") 会将元素显示为过渡的一半。

显然flash-red 只是一个例子——我希望能够用任何动画来做到这一点。

【问题讨论】:

  • 不,你不能那样做。
  • CSS3 动画(和过渡)似乎不够灵活。您可以创建(或搜索)一个 JavaScript 函数或 jQuery 插件来计算和应用受影响的样式,给定 2 个 CSS 规则(一个用于开始状态,一个用于结束状态)和一个百分比值。在某些情况下,为中间状态硬编码一组 CSS 规则可能更容易。
  • 编辑了我的答案以包含一个工作示例,我认为这就是您想要的?

标签: javascript html css css-animations


【解决方案1】:

假设您的元素 gf 上只有一个动画,您可以使用 animation-delayanimation-play-state 简单地控制它:

gf.__proto__.animate = function(percent) {
  this.style["animation-play-state"] = "paused";
  this.style["animation-delay"] = (
    (parseFloat(this.style["animation-duration"] || 1) * -percent) + "s"
  );
};

你可以得到如下计算的样式:

window.getComputedStyle(gf).background

以任意速度步进:

(function animation(time) {
  gf.animate( ((time || 0) % desireSpeed ) / desireSpeed );
  requestAnimationFrame(animation);
})();

注意:这将覆盖 css 中的动画延迟,因此您可能希望将其保留在变量中并将其作为偏移量添加到 gf.__proto__.animate()

【讨论】:

    【解决方案2】:

    使用内置动画:

    很遗憾,没有

    transition 的内部结构不会暴露给 JavaScript,因此您无法利用它来设置或获取数据。这是有目的的 - 如果数据被暴露,这将意味着效率降低,因为必须更新 JavaScript 事件队列。由于 JS 是单线程的,并且动画在单独的线程上运行,您很快就会失去它在单独线程上在内部编译代码中运行的好处。

    但是,您可以进行自己的转换。这涉及到您自己的计算转换。

    这并不像听起来那么复杂,因为您只需使用插值公式来制作动画:

    current = source + (destination - source) * fraction;
    

    例如,对于颜色,您可以将其与颜色组件一起使用。假设我们有属性为rgb 的颜色对象:

    var color1 = {r: 100, g: 200, b: 55};  //some random color
    var color2 = {r: 0,   g: 100, b: 100};
    
    var fraction = 0.5; //0-1
    

    这里当前的 RGB 为:

    r = color1.r + (color2.r - color1.r) * fraction;
    g = color1.g + (color2.g - color1.g) * fraction;
    b = color1.b + (color2.b - color1.b) * fraction;
    

    职位:

     var pos1x = 100;
     var pos1y = 100;
     var pos2x = 500;
     var pos2y = 250;
    
    var fraction = 1; //0-1
    
    posX = pos1x + (pos2x - pos1x) * fraction;
    posY = pos1y + (pos2y - pos1y) * fraction;
    

    等等。

    通过制作包装函数,您可以轻松计算这些函数,甚至可以将它们放入循环中以对其进行动画处理。

    设置颜色 1 和颜色 2 之间过渡的示例函数。 风格可以是ie。 backgroundColorcolor 等:

    function setColor(element, style, color1, color2, fraction) {
    
        var r = color1.r + (color2.r - color1.r) * fraction;
        var g = color1.g + (color2.g - color1.g) * fraction;
        var b = color1.b + (color2.b - color1.b) * fraction;
    
        element.style[style] = 'rgb(' + (r|0) + ',' + (g|0) + ',' + (b|0) + ')';
    }
    

    r|0 只是简单地去掉了小数部分)。

    对于位置,例如:

    var pos1 = {x: 0, y: 0};
    var pos2 = {x: 200, y: 0};
    
    function setPosition(element, pos1, pos2, fraction) {
    
        var x = pos1.x + (pos2.x - pos1.x) * fraction;
        var y = pos1.y + (pos2.y - pos1.y) * fraction;
    
        element.style.left = x + 'px';
        element.style.top = y + 'px';
    }
    

    一个简单的演示(使用 Chrome 或 Aurora 23 来查看滑块,滑块在 FF 23 的下一个版本中提供)。

    Fiddle

    在源和命运之间的任何点手动设置过渡,或为它们设置动画。

    【讨论】:

    • 您知道任何允许您为属性指定两个类的库吗?所以像getMixed(el, 'classA', 'classB', .4)这样的东西?如果是这样,那可能会奏效。
    • @AaronYodaiken 我不知道有一个库可以做到这一点并允许设置分数。您也许可以使用 f.ex。 window.getComputedStyle(elem, null).getPropertyValue('width') 提取值。挑战在于目标样式必须属于作为 DOM 一部分的元素才能获得计算样式,因此它最终会变得像 hack-ish,因为您需要设置 dest。类,获取样式,然后将其设置回源。此外,从源类返回的样式是最新的,因此您需要缓存初始状态的样式。
    【解决方案3】:

    是的, 您可以覆盖动画的持续时间或时间。希望我明白你想做什么:

    http://jsfiddle.net/SEHyW/

    var gf = document.querySelector("#goflash"),
        animationDuration = '1s'
    
    gf.classList.remove("anm-flash");
    setTimeout(function() {
      gf.classList.add("anm-flash");
      gf.style["-webkit-animation-duration"] = animationDuration;
    }, 1000);
    

    【讨论】:

      【解决方案4】:

      你不能随心所欲。

      您唯一的可能是在给定延迟后更改播放状态。

      在您的情况下,由于动画持续 0.5 秒,要获得 50% 的动画,您应该将超时设置为 0.25 秒,然后设置 animation-play-state : paused。

      当然,这不会是 50%,不要相信这种方法的精确度。

      编辑

      为 webkit 添加了演示:

      fiddle

      HTML 很简单

      <div id="goflash">TEST</div>
      <input type="button" value="animate" onclick="animate()">
      

      而且 CSS 很简单

      #goflash {
          width: 200px;
          height: 200px;
          left: 35px;
          top: 35px;
          position: absolute;
          background-color: red;
      }
      .anm-flash {
          -webkit-animation-name: flash;
          -webkit-animation-duration: 5s;
          -webkit-animation-timing-function: linear;
          -webkit-animation-iteration-count: 1;
      }
      
      @-webkit-keyframes flash {
          from {   -webkit-transform: rotate(0deg);
                   background-color: red;        }
          50% {    -webkit-transform: rotate(120deg);
                   background-color: yellow;}
          to {    -webkit-transform: rotate(360deg);
                  background-color: red;
          }
      }
      

      而 javascript 是您提供的扩展:

      function animate () {
          var gf = document.querySelector("#goflash");
          gf.classList.remove("anm-flash");
          setTimeout(function() {
              gf.classList.add("anm-flash");
              gf.style.webkitAnimationPlayState = "running";
          }, 50);
          setTimeout(function() {
              gf.style.webkitAnimationPlayState = "paused";
          }, 2550);
      }
      

      你重置了类,在一个小的暂停后开始动画,并在开始后计算延迟,你停止它。 由于动画时间为 5 秒,初始延迟为 50 毫秒,因此第二次延迟必须为 (5000/2) + 50。 由于您现在已将播放状态设置为暂停,因此要重新运行动画,您必须再次将状态设置为运行。

      【讨论】:

        【解决方案5】:

        也许使用 CSS DOM 来解析动画的意图(如果这可能吗?),然后在 JavaScript 中重建所有内容。

        但这绝非易事!

        我想知道 CSS 预处理器是否有助于构建这样的代码。这都是理论上的。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-05-06
          • 1970-01-01
          • 1970-01-01
          • 2017-06-28
          • 2020-02-02
          • 2016-06-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多