fix: 修复anchorNav组件定位不准问题 (#10219)

Co-authored-by: qinhaoyan <30946345+qinhaoyan@users.noreply.github.com>
This commit is contained in:
qkiroc 2024-05-13 14:57:29 +08:00 committed by GitHub
parent 5b361e135b
commit 6cd5a5975d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -78,54 +78,24 @@ export class AnchorNav extends React.Component<AnchorNavProps, AnchorNavState> {
// 后代节点观察器 // 后代节点观察器
observer: MutationObserver; observer: MutationObserver;
sections: {
key: string | number;
element: HTMLDivElement;
}[] = [];
componentDidMount() { componentDidMount() {
// 初始化滚动标识 // 初始化滚动标识
this.setState({fromSelect: false}); this.setState({fromSelect: false});
const sectionRootDom = const sectionRootDom =
this.contentDom && (this.contentDom.current as HTMLElement); this.contentDom && (this.contentDom.current as HTMLElement);
this.updateSectionOffset(sectionRootDom, false); sectionRootDom.addEventListener('scroll', this.scrollToNav);
this.observer = new MutationObserver(() =>
// TODO: 牺牲性能
this.updateSectionOffset(sectionRootDom, true)
);
this.observer.observe(sectionRootDom, {childList: true, subtree: true});
} }
componentWillUnmount() { componentWillUnmount() {
if (this.contentDom && this.contentDom.current) { if (this.contentDom && this.contentDom.current) {
this.contentDom.current.removeEventListener('scroll', this.scrollToNav); this.contentDom.current.removeEventListener('scroll', this.scrollToNav);
} }
this.observer && this.observer.disconnect();
}
updateSectionOffset(parentNode: HTMLElement, inited: boolean) {
const offsetArr: Array<SectionOffset> = [];
const {children, active} = this.props;
if (!inited) {
// add scroll event
parentNode.addEventListener('scroll', this.scrollToNav);
}
// 收集段落区域offsetTop
children &&
React.Children.forEach(
children,
(section: React.ReactNode, index: number) => {
offsetArr.push({
key: (section as JSX.Element).props.name,
offsetTop: (parentNode.children[index] as HTMLElement).offsetTop
});
}
);
this.setState(
{
offsetArr
},
!inited ? () => active && this.scrollToSection(active) : undefined
);
} }
@autobind @autobind
@ -141,25 +111,24 @@ export class AnchorNav extends React.Component<AnchorNavProps, AnchorNavState> {
const isReachBottom = scrollTop + clientHeight >= scrollHeight; const isReachBottom = scrollTop + clientHeight >= scrollHeight;
// 判断scrollTop所在区域 // 判断scrollTop所在区域
const offsetArr = this.state.offsetArr; const firstSection = this.sections[0];
const firstSection = offsetArr[0]; const lastSection = this.sections[this.sections.length - 1];
const lastSection = offsetArr[offsetArr.length - 1];
// 首层偏移 // 首层偏移
const offset = scrollTop + firstSection.offsetTop; const offset = scrollTop + firstSection.element.offsetTop;
// 首层 // 首层
if (offset <= firstSection.offsetTop) { if (offset <= firstSection.element.offsetTop) {
this.fireSelect(firstSection.key); this.fireSelect(firstSection.key);
} }
// 最后一层 // 最后一层
else if (isReachBottom || offset >= lastSection.offsetTop) { else if (isReachBottom || offset >= lastSection.element.offsetTop) {
this.fireSelect(lastSection.key); this.fireSelect(lastSection.key);
} else { } else {
// 段落区间判断 // 段落区间判断
offsetArr.forEach((item, index) => { this.sections.forEach((item, index) => {
if ( if (
offset >= item.offsetTop && offset >= item.element.offsetTop &&
offset < offsetArr[index + 1].offsetTop offset < this.sections[index + 1].element.offsetTop
) { ) {
this.fireSelect(item.key); this.fireSelect(item.key);
} }
@ -169,14 +138,14 @@ export class AnchorNav extends React.Component<AnchorNavProps, AnchorNavState> {
scrollToSection(key: string | number) { scrollToSection(key: string | number) {
// 获取指定段落的offsettop // 获取指定段落的offsettop
const offsetArr = this.state.offsetArr; const node = find(this.sections, item => item.key === key);
const section = find(offsetArr, item => item.key === key);
const sectionRootDom = const sectionRootDom =
this.contentDom && (this.contentDom.current as HTMLElement); this.contentDom && (this.contentDom.current as HTMLElement);
// 滚动到指定段落 // 滚动到指定段落
section && node &&
(sectionRootDom.scrollTop = section.offsetTop - offsetArr[0].offsetTop); (sectionRootDom.scrollTop =
node.element.offsetTop - this.sections[0].element.offsetTop);
} }
handleSelect(key: string | number) { handleSelect(key: string | number) {
@ -244,7 +213,13 @@ export class AnchorNav extends React.Component<AnchorNavProps, AnchorNavState> {
...section.props, ...section.props,
key, key,
classnames, classnames,
active active,
ref: (props: any) => {
if (props && !this.sections.find(item => item.key === key)) {
// 收集每个段落的真实dom节点
this.sections.push({key: name, element: props.ref.contentDom});
}
}
}); });
} }