如何在内容滚动到视图时淡入
今天,我想向您展示一种以美观而巧妙的方式显示内容的技术 - 通过在内容出现时淡入来显示内容!
时尚滑动部分🎚
让我们从指定所需的 CSS 开始。我们创建两个类 - 一个fade-in-section
基类和一个is-visible
修饰类。当然,您可以随意命名它们。
类fade-in-section
应该隐藏我们的组件,而is-visible
类应该显示它。我们将使用 CSS 过渡效果在它们之间进行转换。
代码如下:
.fade-in-section {
opacity: 0;
transform: translateY(20vh);
visibility: hidden;
transition: opacity 0.6s ease-out, transform 1.2s ease-out;
will-change: opacity, visibility;
}
.fade-in-section.is-visible {
opacity: 1;
transform: none;
visibility: visible;
}
这里,我们使用该transform
属性将容器初始向下移动视口的 1/5(或 20 个视口高度单位)。我们还指定初始不透明度为 0。
通过转换这两个属性,我们将获得想要的效果。我们还将visibility
属性从hidden
转换为visible
。
实际效果如下:
看起来很酷吧?现在,如果我们在视口中滚动新的内容块时都能实现这种效果,那该有多酷?
炫耀的部分👋
如果在内容可见时触发事件,岂不是很棒?我们将使用IntersectionObserver
DOM API 来实现该行为。
该IntersectionObserver
API 是一个非常强大的工具,可以追踪屏幕上某些内容是否部分或全部显示。如果您想深入了解,我建议您阅读这篇MDN 文章。
简单概括一下,交叉观察器接受一个 DOM 节点,并在其进入(或退出)视口时调用一个回调函数。它会返回一些位置数据,以及一些有用的属性isIntersecting
,例如 ,用于告诉我们某个元素是否可见。
不过,本文不会深入探讨交叉观察器的其他实用功能,我们只是实现了一个很棒的“进入时淡入”功能。由于我们使用了 React,我们可以编写一个很棒的可复用组件,并在整个应用程序中重复使用。
以下是实现我们组件的代码:
function FadeInSection(props) {
const [isVisible, setVisible] = React.useState(true);
const domRef = React.useRef();
React.useEffect(() => {
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => setVisible(entry.isIntersecting));
});
observer.observe(domRef.current);
return () => observer.unobserve(domRef.current);
}, []);
return (
<div
className={`fade-in-section ${isVisible ? 'is-visible' : ''}`}
ref={domRef}
>
{props.children}
</div>
);
}
下面是实现它的沙盒:
如果您正在寻找复制和粘贴解决方案 - 那就来这里吧。
正在发生的事情——一步一步
如果您想了解发生了什么,我在下面写了一步一步的指南,解释了发生了什么。
首先,我们调用三个内置的 React Hooks - useState
、useRef
和。您可以在文档useEffect
中阅读有关每个 Hooks 的更多信息,但在我们的代码中,我们执行以下操作:
- 创建一个状态变量,指示该部分是否可见
useState
。我们将其默认为false
- 使用以下方式创建对 DOM 节点的引用
useRef
- 创建交叉口观察器并开始观察
useEffect
交叉路口观察器的设置可能看起来有点陌生,但一旦你了解了发生了什么,它就非常简单。
首先,我们创建一个 IntersectionObserver 类的新实例。我们传入一个回调函数,每当注册到此观察器的任何 DOM 元素的“状态”发生改变时(例如,滚动、缩放或屏幕上出现新内容时),都会调用该函数。然后,我们用 告诉观察器实例观察我们的 DOM 节点observer.observe(domRef.current)
。
不过,在完成之前,我们需要稍微清理一下——每次卸载 DOM 节点时,都需要移除它的交集监听器!幸运的是,我们可以从 中返回一个清理函数useEffect
,它会帮我们完成这项工作。
这就是我们在实现结束时所做的useEffect
——返回一个调用unobserve
观察者方法的函数。(感谢Sung Kim在评论区指出这一点!)
我们传递给观察者的回调函数会使用一个条目对象列表进行调用——每次调用该observer.observe
方法时都会返回一个条目对象。由于我们只调用一次,因此可以假设该列表永远只包含一个元素。
isVisible
我们通过调用其设置器(函数)来更新状态变量setVisible
,并将 的值赋给entry.isIntersecting
。我们可以进一步优化,只调用一次,这样就不会再次隐藏我们已经看到的内容。
我们通过将 DOM 引用附加到实际 DOM 来完成我们的代码 - 通过将其作为ref
prop 传递给我们<div />
。
然后我们可以像这样使用我们的新组件:
<FadeInSection>
<h1>This will fade in</h1>
</FadeInSection>
<FadeInSection>
<p>This will fade in too!</p>
</FadeInSection>
<FadeInSection>
<img src="yoda.png" alt="fade in, this will" />
</FadeInSection>
这就是当您滚动到视图时使内容淡入的方式!
我很想看看您如何以不同的方式实现相同的效果 - 或者是否有任何方法可以优化我编写的代码 - 在评论中。
感谢阅读!
关于可访问性的最后一点
虽然动画看起来很酷,但有些人却不太习惯。对他们来说,动画会损害用户体验。幸运的是,你可以为这些用户实现一个特殊的媒体查询 - 即。你可以(也应该!)在这篇关于此主题的CSS Tricks 文章prefers-reduced-motion
中阅读更多相关信息。