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 |
---|
댓글