FrontEnd

Vue3 - 드래그 시 차트 사라지는 문제

hjkang

현재 대시보드를 개발하면서 기록하고 싶은 이슈가 있어 이 글을 작성하게 되었다. 

개발 중인 대시보드에는 여러 개의 영역이 있고 각 영역마다 드래그&드롭 하여 위치를 변경할 수 있다.
일반적인 영역의 경우 아무 문제 없이 동작하지만, 차트를 포함한 영역의 경우 드래그 중일 때 차트가 사라지고 드롭 후에 차트가 다시 생기는 문제가 있었다.
 
나는 드래그는 vue-draggable-next 라이브러리를 사용하였고, 차트는 vue-chartjs를 사용하여 개발하였는데,
처음에는 라이브러리의 문제인 줄 알고 차트와 드래그 기능을 직접 만들어보았지만 여전히 같은 문제가 발생하였다.
 
원인은 아직 파악하지 못하였고,
드래그 시작 할 때 차트의 canvas 부분을 이미지로 만들어서 그 이미지를 보여주는 방식으로 해결하였다.
 
아래는 해결한 코드!

<template>
  <VueDraggableNext
    v-model="data.filteredWidgets"
    class="dashboard-content"
    ghost-class="ghost"
    chosen-class="active"
    :delay="300"
    :delayOnTouchOnly="true"
    @end="dragEnd"
    @start="dragStart"
  >
    <template v-for="item in data.filteredWidgets">
      <widget :list="[item]" :close-btn="true" @remove="remove">
        <template #content>
          <component
            :is="item.component"
            :key="item.programCode"
            :list="item.data"
          />
        </template>
      </widget>
    </template>
  </VueDraggableNext>
</template>

<script setup>
const dragStart = (type) => {
  const originalContent = document.querySelector(
    ".dashboard-content .sortable-drag",
  );
  if (originalContent) {
    originalContent.style.opacity = 1;
  }

  // 드래그 중인 요소에 차트를 포함한 경우 캔버스 영역을 이미지로 만들어서 해당 이미지로 노출
  const originalWidgetContent =
    originalContent?.querySelector(".widget-content");
  const originalCanvas = originalWidgetContent?.querySelector("canvas");
  const draggedContent = document.querySelector(
    ".dashboard-content > .active .widget-content",
  );
  const draggedCanvas = draggedContent?.querySelector("canvas");

  if (draggedCanvas && originalCanvas) {
    const canvasImg = new Image(
      draggedCanvas.clientWidth,
      draggedCanvas.clientHeight,
    );
    canvasImg.src = draggedCanvas.toDataURL("image/jpeg");
    originalWidgetContent?.replaceChild(canvasImg, originalCanvas);
  }

  const ghostContainer = document.querySelector(".dashboard-content .active");
  const ghostWidgetContainer =
    ghostContainer?.querySelector(".widget-container");
  const ghostContent = ghostWidgetContainer?.querySelector(".widget-content");
  if (ghostContent) {
    ghostContent.style.visibility = "hidden";
    ghostWidgetContainer.classList.add("focus");
    ghostContainer.classList.remove("active");
  }
};

const dragEnd = () => {
  const ghostContainers = document.querySelectorAll(
    ".dashboard-content .widget-container",
  );

  if (ghostContainers?.length) {
    ghostContainers.forEach((item) => {
      const ghostContent = item.querySelector(".widget-content");
      if (ghostContent?.style) {
        item.querySelector(".widget-content").style.visibility = "visible";
      }
      item.classList.remove("focus");
    });
  }
}
</script>