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.
如果您发现任何未解决的边缘情况,请发表评论 :)
我正在尝试实现与 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.
如果您发现任何未解决的边缘情况,请发表评论 :)