Chips 캐러셀 만들기 2부

김민석·2022년 1월 28일
0

한 달에, 한 주제

목록 보기
18/19

첫번째 문제

: 부모보다 큰 자식

<ChipsView>
  <ChipsContainer>
    <Chip/>
    <Chip/>

    /*... many more...*/
    
  </ChipsContainer>
</ChipsView>

이런 형태의 구조다.
많은 캐러셀이 그러듯이 ChipsViewChipsContainer의 일부분만 보여주게 하고,
Container가 좌우로 움직이는 형태로 구현했다.

그런데... Container의 width가 View의 Width와 똑같이 잡혀있었다.
(Chips들이 많아 범위가 넘어가야함에도 불구하고... Overflow)
방식을 고민하다가 youtube에서 구현한 것을 봤더니

display: inline-block;

을 보고 혹시나...? 하는 마음에 적용했더니 chips를 모두 포함하는 width를 가지게 되었다.

참고한 블로그를 읽어보니 inline-block은 고유의 size/spacing을 가질 수 있다는 것을 확인했다.

해결.

두번째 문제

: 우측 맨 끝 chips가 노출되면, 오른쪽 화살표 감추기

(문제를 조금 분리했다.)

IntersectionObserver를 이용하였다.

  useEffect(() => {
    const callback: IntersectionObserverCallback = (entries) => {
      entries.forEach((entry) => {
          switch (entry.target.id) {
          case 'lastChip':
            setIsLastExposed(entry.isIntersecting);
            break;
          case 'firstChip':
            setIsFirstExposed(entry.isIntersecting);
            break;
          default:
            break;
        }
      });
    };
    const options = {
      root: view.current,
      threshold: 1.0,
    };

    const observer = new IntersectionObserver(callback, options);
    if (target.current) {
      const { children } = target.current;
      observer.observe(children[0]);
      observer.observe(children[children.length - 1]);
    }
  }, []);

로직은 다음과 같다.

  1. ChipsView를 Observer에 root Option 대상으로 지정
  2. #firstChip#lastChip을 observer에 등록
  3. entries를 관찰하고 있다가 각 Chip이 안으로 들어오면 state에 따라 화살표를 제거
<ChipsView>  /* <--- Observer :root */
  <ChipsContainer>
    <Chip id="firstChip"/>  /* observe 대상 */
    <Chip/>
    <Chip/>
    <Chip/>
    <Chip id="lastChipChip"/> /*  observe 대상 */
   </ChipsContainer>
</ChipsView>

해결.

세번째 문제

: 화살표 클릭시 양쪽 끝 Chip이 노출되는 순간 transform 제한하기

오른쪽 chip이 노출된 이후에도 고정값으로 정해놓은 320px의 transform이 끝까지 시행되는 것을 알 수 있다. chip이 노출되는 순간 transform은 멈춰야한다.

  const onLeftClick = () => {
    const leftDiff =
      (view.current as HTMLDivElement).getBoundingClientRect().left -
      (container.current as HTMLDivElement).getBoundingClientRect().left;

    setTransform((prev) => prev + (leftDiff < 320 ? leftDiff + 20 : 320));
  };
  const onRightClick = () => {
    const rightDiff =
      (container.current as HTMLDivElement).getBoundingClientRect().right -
      (view.current as HTMLDivElement).getBoundingClientRect().right;

    setTransform((prev) => prev - (rightDiff < 320 ? rightDiff + 20 : 320));
  };
  1. View와 Container에 ref를 붙인다.
  2. 화살표 click시 각각의 left, right position을 가져와서 차이 계산.
  3. 👇
    • 3-1 : left가 클릭되면, container가 왼쪽으로 더 많이 나가 있다는 뜻이므로 (view의 left 값이 더 크므로), 그 차이를 계산하여 tranform할 거리를 계산
    • 3-2 : right이 클릭되면, container가 오른쪽으로 더 많이 나가 있다는 뜻이므로 (container의 right 값이 더 크므로), 그 차이를 계산하여 tranform할 거리를 계산
<ChipsView ref={view}>  
  <ChipsContainer ref={container}>
    <Chip/> 
    <Chip/>
    <Chip/>
    <Chip/>
   </ChipsContainer>
</ChipsView>

완료.


이후는 3으로 분리

0개의 댓글