fix: move to a more stable scroll API to avoid jumping

This commit is contained in:
Jean-Philippe Sirois
2021-06-01 00:08:17 -04:00
parent fdb6083c6b
commit cc740502e9
4 changed files with 276 additions and 85 deletions
+58 -84
View File
@@ -1,4 +1,5 @@
import React, { Component } from "react"
import React, { useEffect, useState } from "react"
import { useDebounce, useWindowScroll } from "react-use"
import cx from "classnames"
import * as SC from "./styles"
@@ -16,10 +17,6 @@ interface IBoxedTitleProps {
content?: React.ReactNode
}
type HeaderBareboneState = {
scrollY: number
}
const BoxedTitle: React.FC<IBoxedTitleProps> = (props) => {
return (
<SC.Box>
@@ -59,86 +56,63 @@ const StickyBoxedTitle: React.FC<IBoxedTitleProps> = (props) => {
}
const StickyContainer: React.FC = ({ children, ...restProps }) => {
return <SC.StickyContainerWrapper {...restProps}>{children}</SC.StickyContainerWrapper>
return (
<SC.StickyContainerWrapper {...restProps}>
{children}
</SC.StickyContainerWrapper>
)
}
const debounce = (fn) => {
let frame
return (...params) => {
if (frame) {
cancelAnimationFrame(frame)
export const HeaderBarebone = (props: IHeaderBareboneProps) => {
const { y } = useWindowScroll()
const [scrollY, setScrollY] = useState(0)
const [, cancel] = useDebounce(
() => {
setScrollY(y)
},
10,
[y]
)
useEffect(() => {
return () => {
cancel()
}
frame = requestAnimationFrame(() => {
fn(...params)
})
}
}
export class HeaderBarebone extends Component<IHeaderBareboneProps, HeaderBareboneState> {
controller: AbortController
state: HeaderBareboneState = {
scrollY: 0
}
constructor(props) {
super(props)
this.tick = this.tick.bind(this)
}
tick() {
if (typeof window !== 'undefined') {
this.setState({
scrollY: window.scrollY
})
}
}
componentDidMount() {
this.controller = new AbortController()
document.addEventListener('scroll', debounce(this.tick), { passive: true, signal: this.controller.signal })
this.tick()
}
componentWillUnmount() {
this.controller.abort()
}
render() {
const props = this.props;
const isBoxed = props.above || props.content
if (this.state.scrollY <= 250) {
return (
<SC.HeaderWrapper className={props.className}>
<SC.Background />
<Container>
{isBoxed && (
<BoxedTitle above={props.above} content={props.content}>
{props.title}
</BoxedTitle>
)}
{!isBoxed && <SC.SingleTitle>{props.title}</SC.SingleTitle>}
</Container>
</SC.HeaderWrapper>
)
} else {
return (
<SC.HeaderWrapperSticky className={props.className}>
<SC.Background />
<StickyContainer>
{isBoxed && (
<StickyBoxedTitle above={props.above}>
{props.title}
</StickyBoxedTitle>
)}
{!isBoxed && <SC.SingleTitle>{props.title}</SC.SingleTitle>}
</StickyContainer>
</SC.HeaderWrapperSticky>
)
}
}
}, [cancel])
const isBoxed = props.above || props.content
const stickyHeader = (
<SC.HeaderWrapperSticky className={props.className}>
<SC.Background />
<StickyContainer>
{isBoxed && (
<StickyBoxedTitle above={props.above}>{props.title}</StickyBoxedTitle>
)}
{!isBoxed && <SC.SingleTitle>{props.title}</SC.SingleTitle>}
</StickyContainer>
</SC.HeaderWrapperSticky>
)
return (
<>
<SC.HeaderWrapper className={props.className}>
<SC.Background />
<Container>
{isBoxed && (
<BoxedTitle above={props.above} content={props.content}>
{props.title}
</BoxedTitle>
)}
{!isBoxed && <SC.SingleTitle>{props.title}</SC.SingleTitle>}
</Container>
</SC.HeaderWrapper>
{scrollY >= 270 && stickyHeader}
</>
)
}
+1 -1
View File
@@ -21,7 +21,7 @@ export const Sidebar = styled.div`
margin: 64px 0;
padding: 0 32px;
position: sticky;
top: 32px;
top: 119px;
&:empty {
display: none;