Pallalax Section

15512 단어 React후크
최근에는 선호하고 구현되지 않았을지도? Pallax Section입니다. 시각 효과 이외에 「화면 내 판정 트리거」가 후일 샘플의 도움이 되기 때문에 소개합니다. 우선은 가장 간단한 toggle class로 실현하는 pallax 시각 효과로 구조를 이해합니다.
code: github/$ yarn 1207






다음과 같이 wrapper 내부는 움직임의 논리에 관여하지 않고 자유롭게 컨텐츠를 배치할 수 있도록 구현합니다.

components/sections/section1.tsx
const View = (props: Props) => (
  <PallalaxSection
    className={props.className}
    toggleClassName={'inArea'}
    topThrethold={100}
  >
    <img src={require('./assets/1.jpg')} width="100%" />
    <h2>Rabbit</h2>
    <p>
      Lorem ipsum dolor sit amet consectetur adipisicing
      elit. Iste reiciendis voluptatum, cum in nulla dolore
      fugiat quis et id. Sunt minima culpa atque qui velit
      aliquid facilis, vero error ad!
    </p>
  </PallalaxSection>
)

Component 개요도



Wrapper는 포함 요소가 화면에 포함되어 있는지 여부를 유지합니다. 화면내에 섹션이 들어갔는지의 여부의 판정은, 섹션의 상변과 하변의 좌표로부터 산출합니다. 이때 좌표 딱 맞는 class 를 toggle 해 버리면, 움직이고 있는 모습이 그다지 보이지 않습니다. 임의의 역치만큼, 화면내 판정의 거리를 좁게 합니다. topThrethold 와 bottomThrethold 가 그 설정치입니다.



usePallalaxSection



이번에 정의한 Custom Hooks입니다. 화면 판정 처리가 혼잡해 있는 것만으로, 그 외는 간단합니다.

components/sections/usePallalaxSection.ts
const usePallalaxSection = (props: Props) => {
  const [state, setCurrent] = useState<State>(...)
  const options = useMemo(...)
  useEffect(...) // 画面内判定
  useEffect(...) // 判定後処理
}

다음의 State 를 보관 유지하고 있습니다.

components/sections/usePallalaxSection.ts
type State = 'OUTSIDE' | 'IN_AREA' | null

화면 내 판정



화면 내 판정 로직입니다. 부하를 줄이기 위해 lodash.throttle 에서 스크롤 처리를 씨닝합니다. 이 throttleInterval도 Optional Injection에서 조정할 수 있도록 해 둡니다.

components/sections/usePallalaxSection.ts
useEffect(
  () => {
    const handleWindowScroll = throttle(() => {
      if (props.ref.current === null) return
      const {
        top,
        height
      } = props.ref.current.getBoundingClientRect()
      const topThrethold = options.topThrethold
      const bottomThrethold = options.bottomThrethold
      const offsetBottom = window.innerHeight - top
      const offsetTop = top + height
      const isOutOfBottom = offsetBottom < 0
      const isOutOfTop = offsetTop < 0
      const isInArea =
        !isOutOfBottom &&
        !isOutOfTop &&
        offsetBottom > bottomThrethold &&
        offsetTop > topThrethold
      if (isInArea) {
        setCurrent('IN_AREA')
      } else {
        setCurrent('OUTSIDE')
      }
    }, options.throttleInterval) // here
    handleWindowScroll()
    window.addEventListener('scroll', handleWindowScroll)
    return () =>
      window.removeEventListener(
        'scroll',
        handleWindowScroll
      )
  },
  [
    props.ref,
    options.topThrethold,
    options.bottomThrethold,
    options.throttleInterval
  ]
)

판정후처리



이 샘플에서는, 출입할 때마다 toggle class 하고 있습니다만, 판정 후처리를 props callback 에 맡길 수도 있습니다.

components/sections/usePallalaxSection.ts
useEffect(
  () => {
    if (props.ref.current === null) return
    if (state === 'IN_AREA') {
      props.ref.current.classList.add(
        props.toggleClassName
      )
    } else {
      props.ref.current.classList.remove(
        props.toggleClassName
      )
    }
  },
  [props.ref, state]
)

래퍼 구성 요소에 적용



처음 화면 내 판정 임계 값 등을 Optional Injection으로 주입합니다. 이와 같이 Wrapper 와 컨텐츠를 분리하는 것으로 rerender 를 억제합니다.

components/sections/pallalaxSection.tsx
export default (props: Props) => {
  const ref = useRef({} as HTMLDivElement)
  usePallalaxSection({
    ref,
    toggleClassName: props.toggleClassName,
    topThrethold: props.topThrethold, // here
    bottomThrethold: props.bottomThrethold, // here
    throttleInterval: props.throttleInterval
  })
  return (
    <section ref={ref} className={props.className}>
      {props.children}
    </section>
  )
}

좋은 웹페이지 즐겨찾기