如何检测 CSS3 动画或网络动画 API 所做的更改?

How to detect changes made by CSS3 animation or web animations API?

如何检测 CSS3 动画或网络动画 (Element.animate) 所做的更改??

(抱歉我的英语不好!这是我在 Whosebug 中的第一个问题)

我知道 MutationObserver,只有当我更改内联样式或使用 requestAnimationFrame 时它才会响应(因为我使用它更改了内联样式)。但是,如果我使用 CSS3 动画或 Web 动画,MutationObserver 不会响应,因为它们不会更改内联样式。

看到这个...这里有两个 div...div1,div2。当div2 的位置改变时,div1 的位置也会改变。但只有当我像我之前所说的那样使用 requestAnimationFrame 时才会发生这种情况。

我的问题是如何为 css3 动画和网络动画 (Element.animate) 执行此操作?

const div1 = document.getElementById('div1');
const div2 = document.getElementById('div2');

/***** Add mutation observer to detect change *****/

const mutation = new MutationObserver(mutations => {
  div1.style.left = div2.style.left;
});

mutation.observe(div2, {
  attributes: true
});

/***** Animation with css *****/

function cssAnimation() {
  div2.style.animation = 'anim 1.5s linear';
}

/***** Animation with web animations *****/

function webAnimation() {
  div2.animate({
    left: [0, '500px']
  }, {
    duration: 1500,
    easing: 'linear'
  });
}

/*****Animation with requestAnimationFrame ******/

// Current left position of div2
const left = 0;

function requestAnimation() {
  // Increase left position 5px per keyframe
  div2.style.left = `${(left += 5)}px`;

  // Increase left position until it reaches to 500px
  if (left < 500) {
    requestAnimationFrame(requestAnimation);
  }
}

function clearAnimations() {
  left = 0;
  div2.style.left = 0;
  div2.style.animation = 'unset';
}
@keyframes anim {
  from {
    left: 0;
  }
  to {
    left: 500px;
  }
}

#div1 {
  background: orange;
  width: 100px;
  height: 100px;
  position: absolute;
  top: 200px;
}

#div2 {
  background: lightgreen;
  width: 100px;
  height: 100px;
  position: absolute;
  top: 100px;
}
<div id="buttons">
  <h3>Animate with...</h3>
  <button onclick='cssAnimation()'>Css3</button>
  <button onclick="requestAnimation()">request animation frame</button>
  <button onclick="webAnimation()">web animations api</button>
  <button id="clear" onclick="clearAnimations()">Clear</button>
</div>

<div id="div1">
  Div1
</div>
<div id="div2">
  div2
</div>

对于 css 动画,您有 animationstart 和 animationend 事件可以帮助您实现您想要实现的目标。但是,没有 animationchangeanimationupdate 事件,据我所知,这是设计使然。如果动画期间没有事件发生,则可以实现完全硬件加速,插值计算直接在 GPU 中完成。因此,请注意,虽然您可以通过 animationstart、animationend 和 requestAnimationFrame 模拟 animationchange 事件的作用,但这可能会影响性能。

你把事情复杂化了。但是要监听动画变化,您可以监听这些事件而不是变异观察器。

animationstart //will fire has soon as the animation starts
animationiteration //will fire if the same animation is looped can be infinity or more then 2
animationend // will fire when the animations have ended

也请使用翻译而不是动画左侧 属性。

您可以使用 requestAnimationFramewindow.getComputedStyle() 在动画过程中获取当前动画样式,请注意,包含 fill:"forward"Element.animate() 调用

var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");

/***** Add mutation observer to detect change *****/

var mutation = new MutationObserver(function(mutations) {
  div1.style.left = div2.style.left;
});
mutation.observe(div2, {
  attributes: true
});

/***** Animation with css *****/

function cssAnimation() {
  div2.style.animation = "anim 1.5s linear forwards";
  let animationFrame;

  function getCurrentStyles() {
    console.log(window.getComputedStyle(div2).left);
    animationFrame = requestAnimationFrame(getCurrentStyles)
  }
  getCurrentStyles();
  div2.addEventListener("animationend", () => cancelAnimationFrame(animationFrame));
}

/***** Animation with web animations *****/

function webAnimation() {
  let animationFrame;

  function getCurrentStyles() {
    console.log(window.getComputedStyle(div2).left);
    animationFrame = requestAnimationFrame(getCurrentStyles)
  }
  getCurrentStyles();
  div2.animate({
    left: [0, "500px"]
  }, {
    duration: 1500,
    fill: "forwards",
    easing: "linear"
  }).onfinish = function() {
    cancelAnimationFrame(animationFrame);
    console.log(window.getComputedStyle(div2).left);
  };
}

/*****Animation with requestAnimationFrame ******/

//Current left position of div2
var left = 0;

function requestAnimation() {
  //Increase left position 5px per keyframe
  div2.style.left = `${(left += 5)}px`;
  console.log(window.getComputedStyle(div2).left);
  //Increase left position until it reaches to 500px
  if (left < 500) {
    requestAnimationFrame(requestAnimation);
  }
}

function clearAnimations() {
  left = 0;
  div2.style.left = 0;
  div2.style.animation = "unset";
}
@keyframes anim {
  from {
    left: 0;
  }
  to {
    left: 500px;
  }
}

#div1 {
  background: orange;
  width: 100px;
  height: 100px;
  position: absolute;
  top: 200px;
}

#div2 {
  background: lightgreen;
  width: 100px;
  height: 100px;
  position: absolute;
  top: 100px;
}
<div id="buttons">
  <h3>Animate with...</h3>
  <button onclick='cssAnimation()'>Css3</button>
  <button onclick="requestAnimation()">request animation frame</button>
  <button onclick="webAnimation()">web animations api</button>
  <button id="clear" onclick="clearAnimations()">Clear</button>
</div>

<div id="div1">
  Div1
</div>
<div id="div2">
  div2
</div>

CSS 动画和 Web 动画都基于将动画播放委托给浏览器的原则。这允许浏览器在可能的情况下 运行 在单独的线程或进程上播放动画,以便它 运行 流畅。尽可能避免在每一帧上从 JavaScript 更新样式。

在您的示例中,您可以简单地同时 运行 两个元素上的动画吗?至少,网络动画允许同步动画。

当其余的 Web 动画 API 发布时,从一个元素复制动画并将它们应用到另一个元素会容易得多,但现在您需要调用 animate 两次.

正如其他人指出的那样,可以观察到动画播放中的重要时刻(开始、结束、重复等),但没有任何事件 运行 在每一帧上。如果你想在每一帧上执行和操作你需要使用 requestAnimationFrame.

如果您在 requestAnimationFrame 中将 div2 传递给 getComputedStyle,您将获得该帧的动画样式,然后您可以将其应用于 div1。 (也就是说,读取 div2.style.left 只会为您提供通过 style 属性指定的样式,但 getComputedStyle(div2).left 将为您提供动画样式,包括 CSS 动画和 Web 动画的样式更改).但是,同样,这会导致性能不佳,并且两个动画不一定会同步,因为 CSS 动画或 Web 动画可能 运行 在不同的线程或进程上。