
本文详细介绍了如何在material-ui (mui) 应用中,利用`usescrolltrigger`钩子结合react的`useref`和`useeffect`,实现一个粘性组件在其父容器滚动到底部时自动隐藏的交互效果。通过动态设置`usescrolltrigger`的目标元素和滚动阈值,开发者可以精确控制粘性元素的显示与隐藏逻辑,从而优化用户体验,避免粘性元素遮挡底部内容。
Material-UI (MUI) 提供了强大的布局组件,其中Box组件通过position="sticky"属性可以轻松实现粘性定位效果。当一个元素被设置为position="sticky"时,它在正常流中表现为相对定位,但在滚动到特定阈值时会“粘”在屏幕的某个位置(如顶部、底部),直到其父容器的边界将其“推”走。
通常,我们会将一个Box元素设置为position="sticky"和bottom={0},使其在滚动时粘在父容器的底部。然而,在某些场景下,我们希望当用户滚动到父容器的底部时,这个粘性元素能够自动隐藏,以避免遮挡父容器的最终内容或提供更流畅的交互体验。
直接使用position="sticky"无法原生实现“在父容器底部时隐藏”的逻辑。MUI的useScrollTrigger钩子是一个强大的工具,它通常用于监听窗口的滚动事件,以触发如App Bar的提升效果或“返回顶部”按钮的显示。然而,其默认行为是监听全局window对象的滚动。我们的挑战在于如何让useScrollTrigger监听特定的父容器,并根据该父容器的滚动位置来控制粘性元素的可见性。
要解决这个问题,我们需要将useScrollTrigger的目标(target)属性指向我们的可滚动父容器。这可以通过React的useRef钩子来获取DOM元素的引用,并结合React.useState和React.useEffect来动态设置useScrollTrigger的target。
首先,构建一个包含可滚动内容的父容器Box和一个粘性子Box。父容器需要设置overflow: 'auto'或overflow: 'scroll'使其可滚动。
import React from 'react';
import { Box, useScrollTrigger } from '@mui/material';
export default function StickyDivControl() {
const parentRef = React.useRef(null); // 创建一个ref来引用父容器
// ... 后续逻辑
return (
<Box
sx={{
width: 400,
height: 300, // 设置一个固定高度,使其可滚动
overflow: 'auto',
border: '1px solid #ccc',
borderRadius: '4px',
}}
ref={parentRef} // 将ref绑定到父容器
>
<ul>
{/* 生成大量内容使父容器可滚动 */}
{Array.from({ length: 100 }, (_, index) => (
<li key={index}>{`Text ${index + 1}`}</li>
))}
</ul>
{/* 粘性元素,初始定位在底部 */}
<Box
position="sticky"
bottom={0}
bgcolor="white"
p={2}
boxShadow={2}
zIndex={100}
>
这是一个粘性元素
</Box>
</Box>
);
}useRef在组件初次渲染时,其.current属性可能为null。而useScrollTrigger的target属性期望一个DOM元素。因此,我们需要在组件挂载后,确保ref.current已经指向了DOM元素时,再将其传递给useScrollTrigger。这可以通过useState和useEffect实现。
import React from 'react';
import { Box, useScrollTrigger } from '@mui/material';
export default function StickyDivControl() {
const parentRef = React.useRef(null);
const [scrollTargetNode, setScrollTargetNode] = React.useState(undefined);
// 在组件挂载后,将ref.current赋值给state,作为useScrollTrigger的target
React.useEffect(() => {
setScrollTargetNode(parentRef.current);
}, []); // 空依赖数组确保只在组件挂载时运行一次
// 配置useScrollTrigger
// target: 监听的滚动元素
// threshold: 滚动多少距离后触发(例如,从底部向上滚动100px时显示)
const showSticky = useScrollTrigger({
target: scrollTargetNode, // 使用state中保存的DOM节点
disableHysteresis: true, // 禁用滞后,滚动一超过阈值就触发
threshold: 100, // 滚动距离父容器底部100px时触发
});
// ... 渲染逻辑
}这里threshold: 100的含义是:当滚动位置超过其父容器的顶部(或从顶部开始计算)100像素时,showSticky变为true。为了实现“在父容器底部时隐藏”,我们需要调整threshold的逻辑,或者更直接地,判断父容器是否滚动到了底部。
然而,useScrollTrigger的threshold默认是基于从顶部滚动的距离。如果我们想在滚动到父容器底部时隐藏,那么useScrollTrigger可能不是最直观的工具,因为它主要用于监听从顶部开始的滚动距离。
更准确的思路是: showSticky为true时,表示用户已经向下滚动了一段距离(超过threshold)。如果我们想在父容器滚动到底部时隐藏粘性元素,那么showSticky应该控制粘性元素的显示,即当showSticky为true时显示,当showSticky为false时(在顶部或者在底部)隐藏。
让我们重新思考threshold的设定。如果threshold设为100,意味着当从父容器顶部向下滚动超过100px时,showSticky变为true。这可以用来控制当用户开始滚动时显示粘性元素,而在顶部时隐藏。
为了实现“在父容器底部时隐藏”,我们可以利用useScrollTrigger的返回值来控制粘性元素的条件渲染或者样式。
当showSticky为true时,粘性元素显示。当showSticky为false时,粘性元素隐藏。 那么,threshold的设置就决定了何时showSticky变为true。如果我们将threshold设置为一个较小的值(例如1),那么只要用户开始滚动,showSticky就变为true。当用户滚动到顶部时,showSticky变为false。
但我们想要的是:当滚动到父容器底部时,粘性元素隐藏。 useScrollTrigger本身没有直接提供“滚动到元素底部”的判断。我们需要结合scrollTargetNode的scrollHeight、clientHeight和scrollTop属性来手动判断是否到达底部。
优化方案:结合useScrollTrigger和手动滚动判断
import React from 'react';
import { Box, useScrollTrigger } from '@mui/material';
export default function StickyDivControl() {
const parentRef = React.useRef(null);
const [scrollTargetNode, setScrollTargetNode] = React.useState(undefined);
const [isAtBottom, setIsAtBottom] = React.useState(false);
React.useEffect(() => {
setScrollTargetNode(parentRef.current);
}, []);
// useScrollTrigger在这里可以用于判断是否在顶部
const isScrollingDown = useScrollTrigger({
target: scrollTargetNode,
disableHysteresis: true,
threshold: 1, // 只要滚动超过1px就触发
});
// 监听父容器的滚动事件来判断是否到达底部
React.useEffect(() => {
const parentElement = parentRef.current;
if (!parentElement) return;
const handleScroll = () => {
const { scrollTop, scrollHeight, clientHeight } = parentElement;
// 当滚动到底部时,scrollTop + clientHeight === scrollHeight
// 留一点裕量,防止浮点数误差
const bottomReached = scrollTop + clientHeight >= scrollHeight - 5;
setIsAtBottom(bottomReached);
};
parentElement.addEventListener('scroll', handleScroll);
// 初始加载时也检查一次
handleScroll();
return () => {
parentElement.removeEventListener('scroll', handleScroll);
};
}, [scrollTargetNode]); // 依赖scrollTargetNode,确保在节点可用时才添加监听器
// 粘性元素是否应该显示:
// 1. 正在向下滚动 (isScrollingDown为true)
// 2. 并且没有滚动到父容器底部 (isAtBottom为false)
const shouldShowSticky = isScrollingDown && !isAtBottom;
return (
<Box
sx={{
width: 400,
height: 500, // 增加高度以便更明显地滚动
overflow: 'auto',
border: '1px solid #ccc',
borderRadius: '4px',
}}
ref={parentRef}
>
<ul>
{Array.from({ length: 100 }, (_, index) => (
<li key={index}>{`Text ${index + 1}`}</li>
))}
</ul>
{/* 根据shouldShowSticky的值条件渲染粘性元素 */}
{shouldShowSticky && (
<Box
position="sticky"
bottom={0}
bgcolor="white"
p={2}
boxShadow={2}
zIndex={100}
>
当父容器滚动到底部时隐藏我
</Box>
)}
</Box>
);
}在这个优化方案中:
下面是一个完整的React组件示例,展示了如何实现当父容器滚动到底部时,粘性元素自动隐藏的功能。
import React from 'react';
import { Box, useScrollTrigger, Typography } from '@mui/material';
export default function StickyDivWithDynamicHide() {
const parentRef = React.useRef(null);
const [scrollTargetNode, setScrollTargetNode] = React.useState(undefined);
const [isAtBottom, setIsAtBottom] = React.useState(false);
// 1. 在组件挂载后,将ref.current赋值给state,作为useScrollTrigger的target
React.useEffect(() => {
setScrollTargetNode(parentRef.current);
}, []);
// 2. 使用useScrollTrigger判断是否已经开始向下滚动(离开顶部)
// threshold: 1 表示只要滚动超过1px就触发,即离开顶部
const isScrollingDown = useScrollTrigger({
target: scrollTargetNode,
disableHysteresis: true, // 禁用滞后,滚动一超过阈值就触发
threshold: 1,
});
// 3. 监听父容器的滚动事件,判断是否到达底部
React.useEffect(() => {
const parentElement = parentRef.current;
if (!parentElement) return;
const handleScroll = () => {
const { scrollTop, scrollHeight, clientHeight } = parentElement;
// 判断是否到达底部,留一点误差裕量
const bottomReached = scrollTop + clientHeight >= scrollHeight - 5;
setIsAtBottom(bottomReached);
};
parentElement.addEventListener('scroll', handleScroll);
// 初始加载时也检查一次,以防内容不足无法滚动
handleScroll();
return () => {
parentElement.removeEventListener('scroll', handleScroll);
};
}, [scrollTargetNode]); // 依赖scrollTargetNode,确保在节点可用时才添加监听器
// 4. 决定粘性元素是否显示:
// 只有当用户正在向下滚动(离开顶部)并且没有到达父容器底部时,才显示粘性元素。
const shouldShowSticky = isScrollingDown && !isAtBottom;
return (
<Box
sx={{
width: 400,
height: 500, // 设置一个固定高度,使其可滚动
overflow: 'auto',
border: '1px solid #ccc',
borderRadius: '4px',
p: 2,
}}
ref={parentRef} // 将ref绑定到父容器
>
<Typography variant="h6" gutterBottom>滚动内容区域</Typography>
<Typography variant="body2" color="text.secondary">
请向下滚动,观察底部粘性元素的行为。
</Typography>
<ul>
{/* 生成大量内容使父容器可滚动 */}
{Array.from({ length: 100 }, (_, index) => (
<li key={index}>{`列表项 ${index + 1}`}</li>
))}
</ul>
<Typography variant="h6" mt={4}>区域底部内容</Typography>
<Typography variant="body2" color="text.secondary">
这是父容器底部的额外内容,当滚动到这里时,粘性元素会隐藏。
</Typography>
{/* 根据shouldShowSticky的值条件渲染粘性元素 */}
{shouldShowSticky && (
<Box
position="sticky"
bottom={0}
left={0} // 确保粘性元素在父容器内水平对齐
right={0} // 确保粘性元素在父容器内水平对齐
bgcolor="white"
p={2}
boxShadow={3} // 增加阴影效果
zIndex={100}
textAlign="center"
sx={{ borderTop: '1px solid #eee' }} // 增加顶部边框
>
<Typography variant="body1" color="primary">
我是一个粘性元素,滚动到底部时会消失!
</Typography>
</Box>
)}
</Box>
);
}通过巧妙地结合MUI的useScrollTrigger、React的useRef和useEffect,并辅以对DOM滚动属性的监听,我们可以精确控制粘性元素在父容器内的显示与隐藏逻辑。这种方法不仅解决了特定场景下的交互需求,也展示了React Hooks在处理复杂UI状态和DOM交互时的强大灵活性。理解并运用这些技术,能够帮助开发者创建更加动态和用户友好的MUI应用。
以上就是利用MUI useScrollTrigger实现粘性组件在父容器底部自动隐藏的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号