마우스 드래그 이벤트로 캐러셀 만들기

리액트만패는젊은이·2024년 10월 16일

CSS Playground

목록 보기
5/7
post-thumbnail

일단 마우스 드래그 이벤트를 구현하기 위해서는 해당 이미지에 직접 드래그를 하려 하면 이미지가 움직인다. 브라우저의 기본 클릭이벤트가 발생하면서 드래그 동작을 방해하기 때문에 이를 방지해야하는코드는 아래에 적어둔다

// 아래 함수를 이미지 태그에 집어넣어 드래그가 시작할때 
// 브라우저에서 발생하는 기본동작을 막아주는 코드를 삽입한다
 const handleDragStart = (e: React.DragEvent) => {
        e.preventDefault();
    };

 <Slide key={index} src={image} alt={`Slide ${index + 1}`} onDragStart={handleDragStart} />

드래그 이벤트 관련

  // 마우스 드래그 시작될때 호출, 드래그 상태를 true로 바꾸고
  // startX에 마우스 클릭위치를 저장한다
  const handleMouseDown = (e: React.MouseEvent): void => {
        setIsDragging(true);
        setStartX(e.clientX);
    };
  // 드래그 중일때 호출
  // 현재 마우스 위치와 시작위치의 차이를 계산
  // dragOffset을 업데이트한다
  const handleMouseMove = (e: React.MouseEvent): void => {
        if (!isDragging) return;
        const offset = e.clientX - startX;
        setDragOffset(offset);
    };

  // 드래그가 끝날때 호출
  // dragOffset의 값이 정해둔 기준치 (100px)이상이면 
  // 슬라이드를 이동시키고 0으로 초기화
  const handleMouseUp = (): void => {
        setIsDragging(false);
        if (Math.abs(dragOffset) > 100) {
            if (dragOffset > 0) {
                prevSlide();
            } else {
                nextSlide();
            }
        }
        setDragOffset(0);
    };

전체 코드

'use client';
import React, { useState, useRef } from 'react';
import styled from 'styled-components';

const Container = styled.div`
    width: 600px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    margin-bottom: 400px;
`;

const Title = styled.p`
    margin-bottom: 20px;
    margin-top: 20px;
    font-size: 24px;
    text-align: center;
`;

const CarouselWrapper = styled.div`
    display: flex;
    align-items: center;
    position: relative;
    width: 600px;
    height: 300px;
    overflow: hidden;
    cursor: grab;
`;

interface CarouselTrackProps {
    currentIndex: number;
    isDragging: boolean;
    dragOffset: number;
}

const CarouselTrack = styled.div<CarouselTrackProps>`
    display: flex;
    transition: ${({ isDragging }) => (isDragging ? 'none' : 'transform 0.5s ease-in-out')};
    transform: ${({ currentIndex, dragOffset }) => `translateX(calc(-${currentIndex * 600}px + ${dragOffset}px))`};
`;

const Slide = styled.img`
    min-width: 600px;
    height: auto;
`;

const Button = styled.button`
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    background-color: rgba(255, 255, 255, 0.7);
    border: none;
    font-size: 24px;
    cursor: pointer;
    padding: 10px;
    border-radius: 50%;
    z-index: 10;
`;

const PrevButton = styled(Button)`
    left: 10px;
`;

const NextButton = styled(Button)`
    right: 10px;
`;

const CursorCarousel: React.FC = () => {
    const images: string[] = [
        'https://via.placeholder.com/600x400/FF5733/FFFFFF?text=Slide+1',
        'https://via.placeholder.com/600x400/33FF57/FFFFFF?text=Slide+2',
        'https://via.placeholder.com/600x400/3357FF/FFFFFF?text=Slide+3',
    ];

    const [currentIndex, setCurrentIndex] = useState<number>(0);
    const [isDragging, setIsDragging] = useState<boolean>(false);
    const [startX, setStartX] = useState<number>(0);
    const [dragOffset, setDragOffset] = useState<number>(0);

    const carouselRef = useRef<HTMLDivElement>(null);

    const nextSlide = (): void => {
        setCurrentIndex((prevIndex) => (prevIndex + 1) % images.length);
    };

    const prevSlide = (): void => {
        setCurrentIndex((prevIndex) => (prevIndex - 1 + images.length) % images.length);
    };

    const handleMouseDown = (e: React.MouseEvent): void => {
        setIsDragging(true);
        setStartX(e.clientX);
    };

    const handleMouseMove = (e: React.MouseEvent): void => {
        if (!isDragging) return;
        const offset = e.clientX - startX;
        setDragOffset(offset);
    };

    const handleMouseUp = (): void => {
        setIsDragging(false);
        if (Math.abs(dragOffset) > 100) {
            if (dragOffset > 0) {
                prevSlide();
            } else {
                nextSlide();
            }
        }
        setDragOffset(0);
    };

    // 이미지 클릭 시 드래그 방지
    const handleDragStart = (e: React.DragEvent) => {
        e.preventDefault();
    };

    return (
        <Container>
            <Title>Mouse Cursor Carousel</Title>
            <CarouselWrapper
                ref={carouselRef}
                onMouseDown={handleMouseDown}
                onMouseMove={handleMouseMove}
                onMouseUp={handleMouseUp}
                onMouseLeave={handleMouseUp}
            >
                <CarouselTrack currentIndex={currentIndex} isDragging={isDragging} dragOffset={dragOffset}>
                    {images.map((image, index) => (
                        <Slide key={index} src={image} alt={`Slide ${index + 1}`} onDragStart={handleDragStart} />
                    ))}
                </CarouselTrack>
                <PrevButton onClick={prevSlide}>&#10094;</PrevButton>
                <NextButton onClick={nextSlide}>&#10095;</NextButton>
            </CarouselWrapper>
        </Container>
    );
};

export default CursorCarousel;
profile
front-end-developer

0개의 댓글