리액트로 수평 스크롤 값을 활용하여 블록 형태의 div 스타일링 하기

미연·2022년 5월 10일
2
post-thumbnail

x축 쪽으로(수평적으로) 스크롤 할 수 있어야 한다!

  • 스크롤이 앞에 있을 때는 뒷부분이 블록 형태로,
  • 스크롤이 뒤에 있을 때는 앞부분이 블록 형태로 있어야 한다.

1. 스크롤값을 담는 state

const [scrollX, setScrollX] = useState(0)
const [maxScrollX, setMaxScrollX] = useState(0)

x축으로 스크롤을 할 때, 스크롤값을 담는 state와 그 스크롤값의 최대값을 담는 state를 선언한다.

2. 스크롤값을 측정하는 useEffect

useEffect(() => {
    const scrollTag = document.getElementsByClassName('class_list_table_box')[0]

    scrollTag.addEventListener('scroll', function () {
      // 현재 스크롤 수치값
      setScrollX(Math.ceil(this.scrollLeft))
      // 현재 스크롤구역의 width에서 스크롤 수치 최대값
      setMaxScrollX(scrollTag.scrollWidth - scrollTag.clientWidth)
    })

    // clean up function
    scrollTag.removeEventListener('scroll', function () {
      setScrollX(Math.ceil(this.scrollLeft))
      setMaxScrollX(scrollTag.scrollWidth - scrollTag.clientWidth)
    })
})

스크롤 이벤트가 어디서 어떻게 발생하는지를 useEffect 안의 이벤트리스너를 통하여 조작해 주었다.

  1. class_list_table_box 클래스명의 div에 overflow: scroll; 스크롤 속성을 주었다. 이것을 변수 scrollTag로 지정하였다.
  2. 변수 scrollTag에 스크롤 addEventListener 를 달았다.
  3. addEventListener를 달았으니 클린업 메서드인 removeEventListener도 달아주었다.
  4. addEventListenerremoveEventListener 모두 같은 콜백함수를 담고 있다. 이를 리팩토링 하고자 별도로 메서드를 분리해 주었으나, this 때문에 되지 않았던 것으로 기억한다.
  5. 여러 시도를 해보았음에도.. 구현하고자 하는 스크롤 이벤트에 버그가 생겼었다. ㄱ-
  • this를 selfThis 변수로 담고, 콜백함수에 파라미터로 그대로 받아 시도 -> X
  • scrollTag를 따로 빼서 콜백함수 안에 넣고 this를 잡아서 시도 -> X

3. 스크롤값을 계산하는 이벤트리스너의 콜백함수

조금만 더 자세하게 설명해 보자면?!

scrollTag.addEventListener('scroll', function () {
      // 현재 스크롤 수치값
      setScrollX(Math.ceil(this.scrollLeft))
      // 현재 스크롤구역의 width에서 스크롤 수치 최대값
      setMaxScrollX(scrollTag.scrollWidth - scrollTag.clientWidth)
})

setScrollX(Math.ceil(this.scrollLeft))

  1. scrollTag 요소에서 스크롤이 발생한다.

  2. 이를 this로 잡아주고, scrollLeft 특성을 이용하여 스크롤값을 잡아 준다.

    scrollLeft : 스크롤이 오른쪽에서 왼쪽으로 향할 때, 오른쪽 시작점은 0에서부터 출발한다. 요소의 끝으로 갈수록 음수가 나온다고 한다.
    (그런데 나는 음수가 아닌, 숫자가 세자리수로 점점 커졌었다..!)

  3. 앞쪽의 블록 형태를 위함
    소수점으로 아주 세세하게 나오길래, Math.ceil() 메서드로 소수점을 없애고 반올림을 해주었다. 이를 스크롤값 state scrollX에 담았다.

setMaxScrollX(scrollTag.scrollWidth - scrollTag.clientWidth)

  1. 뒤쪽의 블록 형태를 위함
    뒤쪽에도 블록 형태가 있기 때문에, 스크롤값의 최대값 수치를 잡아 특정값에 도달하면(혹은 도달하지 못함) 블록 형태를 띄어주는 로직을 잡기 위해 구현한 코드이다.
  2. scrollTag 요소의 스크롤이 잡히는 전체 너비 - scrollTag 요소를 스크롤 했을 때 보이는 영역 = 스크롤값의 최대값
  3. 5번을 maxScrollX state에 담았다.

4. 블록 적용 로직

앞쪽의 블록 형태

 <div className={scrollX >= 300 ? 'scroll_block on' : 'scroll_block 0ff'}>
  1. 스크롤값이 300 이상일 때 (300 이상부터 스크롤 끝까지), 앞쪽 영역을 블록 형태로 띄운다.
  2. scroll_block on 으로 클래스명이 바뀌면, postition: sticky 속성으로 인해 블록 형태로 바뀌게 해주었다.

뒤쪽의 블록 형태

<div className={scrollX === 0 || scrollX < maxScrollX ? 'scroll_block on' : 'scroll_block off'}>
  1. scrollX === 0
    뒤쪽의 블록 형태는 스크롤을 하지 않을 때부터 (페이지가 처음 로드 될 때부터) 블록 형태로 있어야 한다.

  2. scrollX < maxScrollX
    현재 스크롤을 하고 있는 스크롤값이, 최대 스크롤값 (스크롤 영역의 끝)에 도달하기 전까지 블록 형태를 띄워 주어야 한다.

  3. 이것도 역시 postition: sticky 속성으로 인해 블록 형태로 바뀌게 해주었다.

profile
FE Developer

0개의 댓글