본문 바로가기
React

React 드래그 앤 드롭으로 puzzle-game 만들기

by Rogan_Kim 2022. 12. 19.
728x90

 

 

 

준비물

1, 9등분한 이미지

2. 사전학습 

 

React 드래그 앤 드롭 구현

핵심키워드 onDragStart, onDragEnter, onDragOver, onDragEnd useRef로 index 위치값 참조 draggable 속성 넣기 > ## DragEvents 우선 4가지 이벤트의 차이를 알아야 합니다. onDragStart

velog.io

 

 

 

코드를 보기 전에 구조 설명

1. typescript를 사용하고 있습니다.

2. css는 styled-components를 사용하고 있습니다.

3. 컴포넌트를 UI,로직을 나눠서 구현하였습니다.

 

 

 

Style.Puzzle.ts

import styled from 'styled-components';

const Container = styled.section`
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  width: 100vw;
  height: 100vh;
  background: linear-gradient(20deg, rgb(33, 33, 33), rgb(66, 66, 66));
`;

const Position = styled.div`
  display: grid;
  justify-content: center;
  align-items: center;
  grid-template-areas:
    'leftTop midTop rightTop'
	'left mid right'
	'leftBot midBot rightBot';
  margin: 150px auto;
  z-index: 3;
  transition: 1s;
  transform: scale(1);
`;

const GRID_AREA: { [key: number]: string } = {
  0: 'leftTop',
  1: 'midTop',
  2: 'rightTop',
  3: 'left',
  4: 'mid',
  5: 'right',
  6: 'leftBot',
  7: 'midBot',
  8: 'rightBot',
};

const PuzzleBox = styled.div<{ gridArea: number; hoverScale: boolean }>`
  grid-area: ${({ gridArea }) => GRID_AREA[gridArea]};
  width: 200px;
  height: 200px;
  background: white;
  border: 1px solid gray;
  cursor: pointer;
  transition: 0.4s;
  :hover {
    transform: ${({ hoverScale }) => hoverScale && 'scale(120%)'};
  }
`;

const PuzzleImg = styled.img`
  width: 100%;
  height: 100%;
`;

const S = {
  Container,
  Position,
  PuzzleBox,
  PuzzleImg,
};

export default S;

 

 

Puzzle.tsx

import S from './Style.Puzzle';
import usePuzzle from './usePuzzle';

export default function Puzzle() {
  const { puzzle, dragEnter, dragStart, drop, scale } = usePuzzle();

  return (
    <S.Container>
      <S.Position>
        {puzzle.map(({ num, url }, idx) => (
          <S.PuzzleBox
            key={idx}
            gridArea={idx}
            hoverScale={scale}
            onDragStart={() => dragStart(idx)}
            onDragEnter={() => dragEnter(idx)}
            onDragOver={e => e.preventDefault()}
            onDragEnd={drop}
            draggable>
            <S.PuzzleImg
              src={process.env.PUBLIC_URL + url}
              alt={`${num}조각`}
            />
            </S.PuzzleBox>
        ))}
        </S.Position>
    </S.Container>
  );
}

 

 

 

usePuzzle.tsx

import { useState, useRef, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

export default function usePuzzle() {
  const navigate = useNavigate();

  const dragItem = useRef<number | null>(null);
  const dragOverItem = useRef<number | null>(null);

  const [scale, setScale] = useState(true);

  const [puzzle, setPuzzle] = useState(() => shuffleArray(URL));

  const dragStart = (idx: number) => {
    dragItem.current = idx;
	setScale(false);
  };
  const dragEnter = (idx: number) => {
    dragOverItem.current = idx;
  };

  const drop = () => {
    setTimeout(() => setScale(true), 800);
	if (
	  typeof dragItem.current === 'number' &&
	  typeof dragOverItem.current === 'number'
	) {
	  const copyListItems = [...puzzle];

      const dragItemContent = [...puzzle][dragItem.current];
      const changTargetItemContent = [...puzzle][dragOverItem.current];
      copyListItems[dragItem.current] = changTargetItemContent;
      copyListItems[dragOverItem.current] = dragItemContent;

      dragItem.current = null;
      dragOverItem.current = null;
      setPuzzle(copyListItems);
	}
  };

  useEffect(() => {
    let pass = '';
    puzzle.forEach(({ num }) => (pass += num));
    if (pass === '698542173') {
	  navigate('/main');
	}
  }, [navigate, puzzle]);

  return { puzzle, dragStart, dragEnter, drop, scale };
}

const URL = [
	{ num: 3, url: '/img/cat/001.jpeg' },
	{ num: 7, url: '/img/cat/002.jpeg' },
	{ num: 1, url: '/img/cat/003.jpeg' },
	{ num: 2, url: '/img/cat/004.jpeg' },
	{ num: 5, url: '/img/cat/006.jpeg' },
	{ num: 4, url: '/img/cat/005.jpeg' },
	{ num: 8, url: '/img/cat/007.jpeg' },
	{ num: 9, url: '/img/cat/008.jpeg' },
	{ num: 6, url: '/img/cat/009.jpeg' },
];

function shuffleArray(array: { num: number; url: string }[]) {
	return array.sort(() => Math.random() - 0.5);
}
728x90

'React' 카테고리의 다른 글

React 무한스크롤 Intersection Observer API  (4) 2022.12.18

댓글