Fade In without jQuery (fadeIn) - CSS 过渡不透明度和显示

Fade In without jQuery (fadeIn) - CSS transition opacity and display

我正在尝试实现与 jQuery 的 fadeIn() 函数相同的效果,其中显示一个元素,然后它的不透明度从 0 变为 1。我需要做它以编程方式(WITHOUT jQuery),我需要元素能够淡出(display:none)然后淡入。

理想的解决方案将使用 CSS 转换来利用硬件加速 - 我可以通过侦听 transitionend 事件使元素成功淡出。然而,淡入淡出被证明是一个挑战,因为以下代码未按预期工作:

fader.style.transition = 'opacity 1s';

const fadeIn = () => {
  fader.style.display = 'block';
  fader.style.opacity = 1;
};

当调用 fadeIn() 时,元素只是快速返回,而不是流畅地动画。我有一个codePen that I've been tinkering with来说明问题。

我的理论是过渡无法在不在 DOM 中的元素上执行,因为我可以通过设置 height:0 而不是 display:none 来使动画工作.也许在我设置 fader.style.display = 'block'; 和实际更新 DOM 之间存在延迟,在此期间我无法转换?

关于这个想法:我似乎也可以通过 setTimeout(() => {fader.style.opacity = 1}, 20} 延迟不透明度变化来让动画工作。然而,这似乎造成了一种竞争条件,因为随着超时持续时间越来越接近 0,动画工作的可靠性越来越低。

请注意,我不想像 this question 的解决方案那样切换 visibility 属性,因为这不会有效地从 DOM 中删除元素。

将 height/width 更改为 0 是一个更可行的选择,但由于元素的高度和宽度未知,因此需要额外的步骤在淡出之前捕获这些值,以便它们可以淡入时重新应用。如果应用程序的不同部分试图更改这些值(例如媒体查询,并且用户在隐藏元素时旋转他们的设备),这看起来很脆弱

下面的代码应该有效地替换 jQuery 的 fadeOut()fadeIn() 函数(非常感谢@Kyle 提供的线索!)。

const fadeIn = (el, ms, callback) => {
  ms = ms || 400;
  const finishFadeIn = () => {
    el.removeEventListener('transitionend', finishFadeIn);
    callback && callback();
  };
  el.style.transition = 'opacity 0s';
  el.style.display = '';
  el.style.opacity = 0;
  requestAnimationFrame(() => {
    requestAnimationFrame(() => {
      el.addEventListener('transitionend', finishFadeIn);
      el.style.transition = `opacity ${ms/1000}s`;
      el.style.opacity = 1
    });
  });
};

const fadeOut = (el, ms, callback) => {
  ms = ms || 400;
  const finishFadeOut = () => {
    el.style.display = 'none';
    el.removeEventListener('transitionend', finishFadeOut);
    callback && callback();
  };
  el.style.transition = 'opacity 0s';
  el.style.opacity = 1;
  requestAnimationFrame(() => {
    requestAnimationFrame(() => {
      el.style.transition = `opacity ${ms/1000}s`;
      el.addEventListener('transitionend', finishFadeOut);
      el.style.opacity = 0;
    });
  });
};

在深入研究 rAF (requestAnimationFrame) 并观看 video on the event loop 之后,这一点变得非常清楚。就在 21:00 附近,我突然想到为什么 rAF 需要嵌套在另一个 rAF 中。

这里是 working example on Codepen.

如果您发现任何未解决的边缘情况,请发表评论 :)