2

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

(对不起我的英语不好!这是我在 Stackoverflow 中的第一个问题)

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

看到这个......这里有两个div...... div1,div2。div1 的位置会随着 div2 的位置变化而变化。但这只有在我使用 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>

4

4 回答 4

1

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

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

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

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

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

于 2017-10-23T03:18:59.100 回答
0

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

于 2017-10-22T04:46:52.807 回答
0

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

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

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

于 2017-10-22T04:46:55.177 回答
0

您可以在动画期间使用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>

于 2017-10-22T05:06:14.273 回答