밝을희 클태

[ Next.JS ] 이미지 슬라이더(image-slider) UX 개선 본문

KEYNUT 프로젝트

[ Next.JS ] 이미지 슬라이더(image-slider) UX 개선

huipark 2024. 8. 18. 15:44

before
after

 기존에 이미지 슬라이더가 이미지 드래그 도중에 아래 또는 위로 스크롤을 하면 스크롤이 됐다. 그래서 터치 이벤트가 시작될 때 기본 동작을 막아버렸는데 이렇게 되면 문제가 이미지 슬라이더 영역에서는 스크롤이 되지 않아 그 외의 영역을 스크롤해야 했다. 더 좋은 방법이 없을까 고민을 하다가 다른 대기업들의 이미지 슬라이더를 한참을 만져보다가 아이디어가 떠올랐다.

 

 터치 이벤트가 시작이 되고 드래그가 일어날때 y, x의 이동거리를 계산해서 y가 더 크다면 페이지를 스크롤하고,  x의 이동거리가 더 크다면 이미지 슬라이더가 드래그되게 하면서 동시에 'touchmove' 이벤트의 기본동작을 막아준다. 정말 별거 아니지만 여기 까지 오는데 시간이 엄청 오래 걸렸다...

 

touchstart event

  const startTouch = useCallback(
    e => {
    // 아래의 hook으로 지금 동작이 스크롤중인지 이미지 슬라이더 드래그 중인지 판단
      // 처음에는 undefined로 초기화
      isScrolling.current = undefined;
      setIsTransitioning(true);
      travelRatio.current = 0;
      // 터치 좌표를 초기화
      initDragPos.current = { x: e.touches[0].clientX, y: e.touches[0].clientY };
      originOffset.current = offset;
      // passive 옵션을 false로 지정해줘야 touchmove의 기본 동작을 막을 수 있다.
      document.addEventListener('touchmove', move, { passive: false });
      document.addEventListener('touchend', end);
    },
    [offset, move, end],
  );

 

touchmove event

  const move = useCallback(
    e => {
      const travelX = e.touches[0].clientX - initDragPos.current.x;
      const travelY = e.touches[0].clientY - initDragPos.current.y;
      // hook의 값이 undefined 이면 y, x의 이동거리를 계산해서 hook의 값을 변경 
      // 페이지 스크롤중이라면 'true' 이미지 슬라이더 드래그중이면 'false'
      if (isScrolling.current === undefined) isScrolling.current = Math.abs(travelY) > Math.abs(travelX);

      if (isScrolling.current === false) {
        if ((travelX > 0 && currentImageIndex === 0) || (travelX < 0 && currentImageIndex === images.length - 1))
          return;
        e.preventDefault();
        if (Math.abs(travelRatio.current) < 0.8) {
          travelRatio.current = travelX / clientWidth;
          if (imageShowRef.current)
            imageShowRef.current.style.transform = `translateX(${originOffset.current + travelX}px)`;
        }
      }
    },
    [clientWidth, currentImageIndex],
  );