React 滚动监听 和 tab 切换
Tabs组件
//Tabs.tsx
import React from "react";
import classNames from "classnames";
import "./index.scss";
interface IProps {
readonly tabs: Array<Itab>;
readonly activeTab: string;
readonly tabActiveHandle: (tabActive: string) => void;
}
interface Itab {
readonly key: string;
readonly label: string;
}
export const Tabs = (props: IProps) => {
const tabsChange = (key: string) => {
props.tabActiveHandle(key);
};
return (
<div className='setting-tab'>
{props.tabs.map((item, i) => (
<div
key={i}
onClick={() => tabsChange(item.key)}
className={classNames(
"tabs",
props.activeTab === item.key && "tabs-active"
)}
>
{item.label}
</div>
))}
</div>
);
};
//Tabs css
.setting-tab {
height: 100%;
width: 136px;
display: flex;
flex-direction: column;
.tabs {
cursor: pointer;
height: 70px;
line-height: 70px;
font-size: 18px;
text-align: center;
position: relative;
top: 0;
right: -1px;
&.tabs-active {
border-right: 1px solid #ffb900;
background: linear-gradient(-90deg, #e4bf7633 -8.46%, #e4bf7600 127.57%);
}
}
}
父组件
// FunctionSetting.tsx
import { useEffect, useState } from "react";
import { Tabs } from "./Tabs";
import { debounce } from "lodash";
import React from "react";
const CONSTANTS = {
CONTENT_ID: "content",
TABS: [
{
key: "tab1",
label: "tab1",
},
{
key: "tab2",
label: "tab2",
},
],
};
export default function FunctionSetting() {
const [activeTab, setActiveTab] = useState<string>(CONSTANTS.TABS[1].key);
// 默认执行一遍
useEffect(() => {
tabActiveHandle(activeTab);
}, []);
// 滚动条监听
useEffect(() => {
const container = document.getElementById(CONSTANTS.CONTENT_ID);
if (container) {
container.addEventListener("scroll", handleScroll);
}
return () => {
if (container) {
container.removeEventListener("scroll", handleScroll);
}
};
}, []);
// 滚动函数
const handleScroll = debounce(() => {
const containerId = CONSTANTS.CONTENT_ID;
const reversedTabs = [...Object.values(CONSTANTS.TABS)].reverse();
// 使用循环检查每个 tab 是否在视口中
for (const tab of reversedTabs) {
if (isElementInViewport(containerId, tab.key)) {
setActiveTab(tab.key);
break; // 找到第一个符合条件的 tab,跳出循环
}
}
}, 100);
// 判断元素是否在容器内
const isElementInViewport = (boxId: string, elId: string) => {
const container = document.getElementById(boxId);
const el = document.getElementById(elId);
if (container && el) {
const containerRect = container.getBoundingClientRect() || {
top: 0,
bottom: 0,
};
const rect = el.getBoundingClientRect();
return (
rect.top < containerRect.bottom && rect.bottom >= containerRect.top
);
}
return false;
};
// 执行滚动到具体tab
const tabActiveHandle = (tabActive: string) => {
setActiveTab(tabActive);
const targetElement = document.getElementById(tabActive);
if (targetElement) {
targetElement.scrollIntoView({ behavior: "smooth" });
}
};
// 组件1
const renderComponent1 = () => {
const data = [];
for (let i = 1; i <= 10; i++) {
data.push(`数据项 ${i}`);
}
return (
<div>
<h3>组件1</h3>
<ul>
{data.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
};
// 组件2
const renderComponent2 = () => {
return (
<div>
<h3>组件2</h3>
<ul>
<li>123</li>
</ul>
</div>
);
};
return (
<div
style={{
display: "flex",
height: "200px",
backgroundColor: "ghostwhite",
}}
>
<Tabs
tabs={CONSTANTS.TABS}
activeTab={activeTab}
tabActiveHandle={tabActiveHandle}
/>
<div
id={CONSTANTS.CONTENT_ID}
style={{
flex: 1,
overflowY: "auto",
height: "100%",
paddingLeft: "33px",
}}
>
<div id={CONSTANTS.TABS[0].key}>{renderComponent1()}</div>
<div id={CONSTANTS.TABS[1].key}>{renderComponent2()}</div>
</div>
</div>
);
}

本文来自博客园,作者:苏沐~,转载请注明原文链接:https://www.cnblogs.com/sumu80/p/18922596

浙公网安备 33010602011771号