react-drag slider

2024. 3. 4. 12:27react

slick 사용 시 drag-slide가 안됨

 

 

처음에는 react-slider 라이브러리를 사용해서 슬라이드를 만들었다. 그런데 사용자 친구에게 포트폴리오를 보여주니

바로 드래그를 해버리는 게 아닌가...?

그래서 라이브러리를 drag-slide가 자연스럽도록 수정을 하려고 했는데 타 블로그에서 react로 drag-slide를 제작하는 글을 보게 되었다.

 

참고 블로그 : https://velog.io/@tunakim/%EB%A7%88%EC%9A%B0%EC%8A%A4-%EB%93%9C%EB%9E%98%EA%B7%B8%EB%A1%9C-%EC%A2%8C%EC%9A%B0-%EC%8A%A4%ED%81%AC%EB%A1%A4-%EA%B5%AC%ED%98%84-ft.-React

 

마우스 드래그로 좌우 스크롤 구현 (ft. React)

아이템들을 행으로 나열하고 설정해놓은 width를 넘어갈 때 overflow-x: scroll로 스크롤을 만들어 보일 수 있게 구현했습니다. 이때 스크롤 바를 움직이는 게 아닌 모바일 환경에서 옆으로 밀어서 이

velog.io

참고 블로그 : https://velog.io/@yezee/React-drag-slider

 

<React> drag slider

마켓컬리를 클론하면서 느낀거는 마켓컬리는 캐러셀과 슬라이더 천국이다 👼🏻넘치는 패기로 라이브러리 없이 구현해보고 싶었다 하지만 슬라이더의 벽은 높았고... 친절한 다른 블로그를 보

velog.io

 

솔직히 지금까지 라이브러리를 너무 애용한 사람이라 이벤트를 직접 구현한 글을 보니 나도 도전 정신이 생겼다.

위 블로그 글을 참고해서 내 컴포넌트에도 적용해보도록...!

 

 

1. 컴포넌트 생성

: drag-slide 기능을 구현할 컴포넌트를 먼저 생성해준다.

  (이때, teamImages는 외부에서 가져온 데이터)

const DragSlide = ({teamImages}) => {
    return(
        <div>
            {teamImages && (
                <div className="DragSlide">
                    {teamImages.map((images) => (
                        <div className="dragImg" key={images.id}>
                            <img src={`img/${images.img}.png`} alt={images.name} />
                        </div>
                    ))}
                </div>
            )}
        </div>
    );
};
export default DragSlide;
.DragSlide{
    display: flex;
    overflow-x: scroll;
}
.DragSlide::-webkit-scrollbar{
    display: none;
}

 

 

2. useRef 사용해 DOM에 직접 접근하기

블로그 설명에 따르면, 좌우 슬라이드 스크롤의 움직임은 해당 DOM의 scrollLeft로 움직인다고 한다.

(클릭한 시점부터 클릭을 뗀 시점까지)

그래서 해당 DOM의 scrollLeft를 얻기 위해 useRef를 사용해 접근한다고 함.

const DragSlide = ({teamImages}) => {

    const scrollRef = useRef();

    return(
        <div>
            {teamImages && (
                <div className="DragSlide" ref={scrollRef}>
                    {teamImages.map((images) => (
                        <div className="dragImg" key={images.id}>
                            <img src={`img/${images.img}.png`} alt={images.name} />
                        </div>
                    ))}
                </div>
            )}
        </div>
    );
};

 

3. 코드 구현

 

- 사용된 이벤트

 

onMouseDown : 마우스 왼쪽 버튼 누르고 있는 상태

onMouseUp  : 마우스 왼쪽 버튼 뗀 상태

onMouseMove  : 마우스를 움직이는 상태(클릭 여부 상관 X)

onMouseLeave  : DOM에서 마우스가 벗어났는 지 체크하는 이벤트

 

- 사용된 변수

 

DOM.scrollWidth : 스크롤 할 수 있는 총 길이

DOM.clientWidth  : 설정한 max width(화면에 보이는 스크롤 길이)

DOM.scrollLeft  : 스크롤 가장 왼쪽(DOM.scrollLeft = 0)부터 이동한 스크롤 길이 = DOM.scrollLeft길이만큼 스크롤 이동

mouseEvent.pageX  : onMouseDown시 x좌표

import { useRef } from "react";
import "../App.css";
import { useState } from "react";

const DragSlide = ({teamImages}) => {

    const scrollRef = useRef(null);
    const [isDrag, setIsDrag] = useState(false);
    const [startX, setStartX] = useState();

	// onDragStart
    // startX는 현재 클릭한 pageX와 움직인 스크롤 길이 scrollLeft를 합친 값이다.
    // 스크롤이 이동하지 않았을 때는 문제가 없으나 스크롤이 이동된 상태에서 클릭을 한다면
    // 브라우저의 width의 pageX 값이 설정돼 순간적으로 앞쪽으로 스크롤됨
   	// 이를 막기 위해 scrollLeft를 더해 현재 x의 위치를 계산
    const onDragStart = (e) => {
        e.preventDefault();
        setIsDrag(true);
        setStartX(e.pageX + scrollRef.current.scrollLeft);
    };

	// onDragEnd
    // onMouseUp, onMouseLeave 이벤트 발생 시 isDrag를 false로 설정
    const onDragEnd = () => {
        setIsDrag(false);
    };

    const throttle = (func, ms) => {
        let throttled = false;
        return(...args) => {
            if(!throttled){
                throttled = true;
                setTimeout(()=>{
                    func(...args);
                    throttled = false;
                }, ms);
            }
        };
    };

    const onDragMove = (e) => {
        if(isDrag){
            const {scrollWidth, clientWidth, scrollLeft} = scrollRef.current;
            scrollRef.current.scrollLeft = startX - e.pageX;

            if(scrollLeft === 0){
                setStartX(e.pageX);
            } else if(scrollWidth <= clientWidth + scrollLeft){
                setStartX(e.pageX + scrollLeft);
            }
        }
    };

    const delay = 100;
    const onThrottleDragMove = throttle(onDragMove, delay);

    return(
        <div>
            {teamImages && (
                <div
                    className="DragSlide"
                    onMouseDown={onDragStart}
                    onMouseUp={onDragEnd}
                    onMouseLeave={onDragEnd}
                    // onMouseMove
                    // isDrag변수가 true일 때 발생하도록 설정
                    // scrollLeft의 값을 설정: 처음 클릭한 x의 좌표 startX에서 움직이면서 변하는
                    // e.pageX를 빼서 이동한 스크롤 길이를 scrollLeft에 삽입
                    onMouseMove={isDrag ? onThrottleDragMove : null}
                    ref={scrollRef}
                >
                    <div className="dragImgContainer">
                        {teamImages.map((images) => (
                            <div className="dragImg" key={images.id}>
                                <img src={`img/${images.img}.png`} alt={images.name} />
                            </div>
                        ))}
                        {teamImages.map((images) => (
                            <div className="dragImg" key={images.id}>
                                <img src={`img/${images.img}.png`} alt={images.name} />
                            </div>
                        ))}
                    </div>
                </div>
            )}
        </div>
    );
};
export default DragSlide;

 

 

4. 코드 리팩토링하기

평상 시 이벤트 제거

onMouseMove이벤트는 클릭 여부 상관없이 해당 DOM에 위치 시 발생함

이를 막기 위해 isDrag가 false일 때 작동하지 않도록 설정

isDrag ? onThrottleDragMove : null

return(
	···
    <div
        className="DragSlide"
        onMouseDown={onDragStart}
        onMouseUp={onDragEnd}
        onMouseLeave={onDragEnd}
        onMouseMove={isDrag ? onThrottleDragMove : null}
        ref={scrollRef}
    >
    ···
)

 

너무 많은 이벤트 발생

onMouseMove로 수많은 이벤트가 발생

이벤트를 delay시켜 끊어주는 Throttle을 사용해 해결

const delay = 50; // delay 속도는 본인 마음대로 설정하기 원글에서는 100ms 사용함
const onThrottleDragMove = throttle(onDragMove, delay);

 

 

5. 마무리

완성본

제작을 하면서 지금까지 내가 라이브러리에 많이 기대고 있었구나 하는 생각이 많이 들었다.

블로그 원글을 살펴보면 알 수 있겠지만 코드를 완성하고 문제점을 파악 후 수정하고 그걸 기록하는 과정이

우리에게는 고작 블로그 한페이지 정도 분량으로 보인다.

하지만 오류를 찾고 수정하는 과정이 그리 쉬운 길이 아니다...

 

나 또한 6개월간 수업을 들으면서 시간이 없다는 핑계로 라이브러리에 많이 기댄 게 아닌가 싶었다. 자체적으로 코드를 짜볼 생각을 안 했던 것 같다. 물론 고작 6개월 수업 들은 걸로 완벽한 코드가 나오지 않을 것을 알고 있기 때문에 더더욱 손을 대지 않았던 것이겠지만...

라이브러리가 편하게 사용이 가능하기 때문에 라이브러리에 많이 기댄 것도 맞는 것 같다.

그래도 개발자로서 성장을 위해서라면 원글의 개발자님처럼 직접 코드를 짜려는 시도, 완성하려는 노력을 한번이라도 시도해봐야겠다.

 

지금은 포트폴리오를 수정하면서 지원서를 여기저기 넣고 있지만 서류에서 탈락...탈락... 그래도 어딘가에서는 나를 꼭 필요로 할 거라고 믿는다. 물론 잘난 거 하나 없지만 어딘가는 나의 성장 욕구를 알아줄 곳이...있을 것이라...믿..는다...ㅠ

'react' 카테고리의 다른 글

react - 무한 롤링 슬라이드  (0) 2024.03.05
react - hook(useMemo함수, useCallback함수 : 컴포넌트 최적화)  (0) 2024.01.16
react-hook(useRef)  (0) 2024.01.16
react-hook (useState, useEffect)  (0) 2024.01.16