본문 바로가기
내일배움캠프 TIL

2024-01-17 본 캠프 74일차 / 93일차 TIL (react-dnd 활용 영상공유 레이아웃 만들기(2)

by KMS_99 2024. 1. 18.

2024-01-17 본 캠프 74일차 / 93일차 TIL (react-dnd 활용 영상공유 레이아웃 만들기(2)

 

주요 진행사항

- react-dnd 활용 영상공유 레이아웃만들기(2) - grid 변경 

 


(이전) 2024-01-17 본 캠프 74일차 / 93일차 TIL (react-dnd 활용 영상공유 레이아웃 만들기(1)

 

2024-01-16 본 캠프 73일차 / 92일차 TIL (react-dnd 활용 영상공유 레이아웃 만들기)

2024-01-16 본 캠프 73일차 / 92일차 TIL (react-dnd 활용 영상공유 레이아웃 만들기) 주요 진행사항 - react-dnd 활용 영상공유 레이아웃만들기 react-dnd 활용 영상공유 레이아웃만들기 유저가 직접 영상공유

audtjqxx.tistory.com

 

react-dnd 활용 영상공유 레이아웃만들기 (2)

 

drag 이벤트에 대한 부모컨테이너와 아이템의 설정이 완료되었다면 이제 부모컨테이너의 drop 이벤트에 원하는 동작을 구현하면 되겠다.

 

구현해야하는 동작

1. 아이템이 hover 되었을 때 마우스 위치에 따라서 가이드 라인을 보여준다

2. 아이템이 drop 되었을 때 가이드 라인에 따른 gird를 조정하고 해당 위치에 drag 아이템을 위치 시킨다.

 

1. 아이템이 hover 되었을 때 마우스 위치에 따라서 가이드 라인을 보여준다.

 hover: (item, monitor) => {
      if (!dropParentRef.current) {
        return;
      }
      //마우스 호버 아웃 체크
      clearTimeout(hoverTimer?.current!);
      // 부모 컴포넌트의 좌표
      const parentRect = dropParentRef.current.getBoundingClientRect();
      // 현재 마우스 좌표
      const clientOffset = monitor.getClientOffset();
      // 부모 컴포넌트 기준 y축 좌표
      const hoverClientY = clientOffset!.y - parentRect.top;
      // 부모 컴포넌트 기준 x축 좌표
      const hoverClientX = clientOffset!.x - parentRect.left;

      // 좌측
      if (hoverClientX < EDGE_AREA_RATE) {
        if (hoverClientY < EDGE_AREA_RATE) {
          setCurrentGuide("left-top");
          setShowGuide(true);
        } else if (hoverClientY > parentRect.height - EDGE_AREA_RATE) {
          setCurrentGuide("left-bottom");
          setShowGuide(true);
        } else {
          setCurrentGuide("left");
          setShowGuide(true);
        }
      } else if (hoverClientX > parentRect.width - EDGE_AREA_RATE) {
        // 우측
        if (hoverClientY < EDGE_AREA_RATE) {
          setCurrentGuide("right-top");
          setShowGuide(true);
        } else if (hoverClientY > parentRect.height - EDGE_AREA_RATE) {
          setCurrentGuide("right-bottom");
          setShowGuide(true);
        } else {
          setCurrentGuide("right");
          setShowGuide(true);
        }
      } else if (hoverClientY < EDGE_AREA_RATE) {
        setCurrentGuide("top");
        setShowGuide(true);
      } else if (hoverClientY > parentRect.height - EDGE_AREA_RATE) {
        setCurrentGuide("bottom");
        setShowGuide(true);
      } else {
        setCurrentGuide("center");
        setShowGuide(true);
      }

      hoverTimer.current = setTimeout(() => {
        setCurrentGuide(null);
        setShowGuide(false);
      }, 150);
    },
export type GuideStatusType =
  | "top"
  | "right"
  | "bottom"
  | "left"
  | "left-top"
  | "right-top"
  | "left-bottom"
  | "right-bottom"
  | "center";
  
const [currentGuide, setCurrentGuide] = useState<GuideStatusType | null>(
    null
  );
  
const [showGuide, setShowGuide] = useState(false);

const StLayoutGuide = styled.div<{ $guide: GuideStatusType | null }>`
  z-index: 10;
  background: rgba(122, 108, 108, 0.5);
  margin: ${(props) => props.theme.spacing[6]};
  border-radius: ${(props) => props.theme.border.radius[12]};
  position: absolute;
  ${(props) => {
    switch (props.$guide) {
      case "top":
        return "top: 0; bottom: 50%; left: 0; right: 0;";
      case "bottom":
        return "top: 50%; bottom: 0; left: 0; right: 0;";
      case "left":
        return "top: 0; bottom: 0; left: 0; right: 50%;";
      case "right":
        return "top: 0; bottom: 0; left: 50%; right: 0;";
      case "left-top":
        return "top: 0; bottom: 50%; left: 0; right: 50%;";
      case "left-bottom":
        return "top: 50%; bottom: 0; left: 0; right: 50%;";
      case "right-top":
        return "top: 0; bottom: 50%; left: 50%; right: 0;";
      case "right-bottom":
        return "top: 50%; bottom: 0; left: 50%; right: 0;";
      case "center":
        return "top: 0; bottom: 0; left: 0; right: 0;";
      default:
        return "top: unset; bottom: unset; left: unset; right: unset;";
    }
  }}
`;

 

- hover 함수의 monitor를 통해서 현재 마우스의 위치를 가져올 수 있다. 현재 마우스 위치와 부모 컨테이너의 위치를 상대적으로 계산하여 부모 컴포넌트 내에서 마우스 위치값을 가져온다.

 

- 해당 위치를 기준으로 사용자가 설정한 EDGE_AREA_RATE라는 마우스 범위 내에 들어왔을 때 가이드 라인을 보여주도록 설정한다. (useState 이용 가이드 위치 설정 및 visible 설정)

 

- css 이용 그리드를 나타낸다.


2. 아이템이 drop 되었을 때 가이드 라인에 따른 gird를 조정하고 해당 위치에 drag 아이템을 위치 시킨다.

아이템이 drop 되었을 때 display grid의 layout을 변경시키고 해당 위치위치에 아이템을 위치시킨다.

    drop: (item: { id: string }) => {
      const changeGridStyle = getGridStyle(currentGuide!);
      const activeIndex = currentLayoutIndex(currentGuide!);

      // 그리드 설정 시 기존 그리드 형식과 비교하여 다를 경우 video state 초기화

      if (currentGrid !== changeGridStyle) {
        const newVideos = videos.map((video) => {
          return video.consumer.id === item.id
            ? { ...video, isActive: activeIndex }
            : { ...video, isActive: 0 };
        });
        videosChange(newVideos);

        // 그리드 변경
        setCurrentGrid(changeGridStyle);
      } else {
        const newVideos = videos.map((video) => {
          if (video.isActive === activeIndex) {
            return { ...video, isActive: 0 };
          } else {
            return video.consumer.id === item.id
              ? { ...video, isActive: activeIndex }
              : video;
          }
        });
        videosChange(newVideos);
      }
    },
  });
  export type GridStatusType =
  | "center-one"
  | "topBottom-two"
  | "leftRight-two"
  | "edge-four";
  
  const [videos, setVideos] = useState<LayoutConsumersType[]>([]);
  
  const videosChange = (newVideos: LayoutConsumersType[]) => {
    setVideos(newVideos);
  };
  
  const [currentGrid, setCurrentGrid] = useState<GridStatusType | null>(null);

 

 

기존 설정된 레이아웃과 비교하여 이전과 같을 시에는 그리드를 유지하고 이전과 다르면 그리드를 초기화 하고 새로운 그리드로 설정한다.

 

따라서 해당 조건에 따른 video 컴포넌트의 순서와 구성을 변경하도록 로직을 설정한다. (videosChange)

이후 그리드에 변경이 생겼을 때 (이전 그리드와 다를 때) setCurrentGrid (useState)를 통해 그리드를 변경한다.

 

해당 과정을 통한 결과물은 다음과 같다.