Last active
December 17, 2022 14:14
-
-
Save teal-front/df554d858b28f48ad7c15f3ac2d85cf6 to your computer and use it in GitHub Desktop.
common interaction
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import classNames from 'classnames'; | |
/** | |
* 点菜单,滚动到内容区的固定位置 | |
*/ | |
export function ScrollToView() { | |
const header = React.useRef(null); | |
const anchor1 = React.useRef(null); | |
const anchor2 = React.useRef(null); | |
const anchor3 = React.useRef(null); | |
const [fixed, setFixed] = React.useState(false); | |
const [active, setActive] = React.useState(1); | |
React.useEffect(() => { | |
const entryList = [ | |
header.current, | |
anchor1.current, | |
anchor2.current, | |
anchor3.current, | |
] | |
// https://usefulangle.com/post/118/javascript-intersection-observer | |
// https://usefulangle.com/post/113/javascript-detecting-element-visible-during-scroll | |
const observer = new IntersectionObserver(function(entries) { | |
for(const entry of entries) { | |
if (entry.target === header.current) { | |
setFixed(entry.intersectionRatio === 0); | |
} else { | |
for(const anchor of entryList.slice(1).reverse()) { | |
if (entry.target === anchor && entry.intersectionRatio === 1) { | |
setActive(entryList.indexOf(anchor)); | |
} | |
} | |
} | |
} | |
}, {threshold: [0, 1]}); | |
entryList.forEach(entry => { | |
observer.observe(entry); | |
}); | |
}, []); | |
// 使用JS来算出要滚动的高度,而不是使用a[id=anchor],这样会被fixed的菜单挡住一部分 | |
const scrollToAnchor = (dom) => { | |
const { top } = dom.getBoundingClientRect(); | |
const tagHeight = 40; | |
// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView | |
// window.scrollTo(x, y) | |
window.scrollTo(0, top + tagHeight); | |
} | |
return ( | |
<> | |
<div className="header" ref={header}>header</div> | |
{/* .tags本身要占有高度, 避免子元素fixed之后,出现高度的抽空 */} | |
<div className='tags'> | |
<div className={classNames("tags-to-be-fixed", { fixed })}> | |
<div className={classNames("tag", { active: active === 1 })} onClick={() => scrollToAnchor(anchor1.current)}> | |
tag-1 | |
</div> | |
<div className="tag">tag-2</div> | |
<div className="tag">tag-3</div> | |
</div> | |
</div> | |
<div className="content"> | |
<div className="content-1" ref={anchor1}>Lorem, ipsum dolor.</div> | |
<div className="content-2" ref={anchor2}>Lorem, ipsum dolor.</div> | |
<div className="content-3" ref={anchor3}>Lorem, ipsum dolor.</div> | |
</div> | |
</> | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment