Workbench - Fit to View 구현記

Dzeko·2024년 1월 9일
0

개발일지

목록 보기
103/112



워크플로우를 만들다 보면 타일을 많이 쓰게 되는 경우가 있다.
이런 경우 zoom 이 100% 일 때 화면에 다 들어오지 않기 때문에,
워크플로우의 전체적인 흐름을 보려면 zoom out을 여러 번 때려야 한다.

사용자의 요구사항이 생겼다. 전체적인 플로우를 쉽게 보고 싶다는 것이다.
그러기 위해서 버튼 하나로 zoom out을 자동으로 계산해서 보여주어야 했다.

바로 가자

button의 클릭 이벤트에 할당되는 fitToView() 메소드를 만들었다.
먼저 줌 아웃은 최대 10%까지 가능하기 때문에 현 줌 상태가 10% 면 return으로 종료시킨다.

fitToView() {
  if (Number(this.zoomNum) === 10) {
    this.loadingFlag = false;
    return;
  }
  this.loadingFlag = true;
},

첫 번째 조건을 통과하고,
줌 아웃 없이 화면의 위치 조정만으로 타일이 화면 안에 모두 들어오는 경우를 만들 수 있었다.
fitLocation이라는 메소드를 만들어 호출시켰다.

fitLocation() {
  const allTiles = this.graph.getAllTiles();
  const tilesViewInfo = [];
  // 모든 타일의 x,y 좌표, width, height 를 맵핑하여 저장;
  allTiles.forEach(tile => {
    const tileState = this.graph.view.getState(tile);
    tilesViewInfo.push({
      x: tileState.x,
      y: tileState.y,
      width: tileState.width,
      height: tileState.height,
    });
  });
  const tilesX = allTiles.map(tile => this.graph.view.getState(tile).x);
  const tilesY = allTiles.map(tile => this.graph.view.getState(tile).y);
  const minX = Math.min(...tilesX) - 30; // 타일 아이콘 때문에 - 30 한 값이 정확한 값;
  const minY = Math.min(...tilesY);
  const {scrollTop, scrollLeft, offsetHeight, offsetWidth} = this.graph.container;

  // 전체 타일 중 최남단에 있는 타일의 y + height 값;
  const southernmostTileValue = this.findTileAtTheEndByDirection(tilesViewInfo, 'y');
  // 전체 타일 중 최동단에 있는 타일의 x + width 값;
  const easternmostTileValue = this.findTileAtTheEndByDirection(tilesViewInfo, 'x');

  // 현재 보이는 editor bottom y값;
  const southViewPortY = scrollTop + offsetHeight;
  // 현재 보이는 editor right x값;
  const eastViewPortX = scrollLeft + offsetWidth;

  // x축의 경우 panel 도 고려해야 한다;
  let panelWidth = document.getElementsByClassName('panel-right')[0].offsetWidth;
  // 패널 토글에 따른 너비 조정;
  if (document.getElementsByClassName('toggle-button').length > 0) {
    const panelCloseState = document.getElementsByClassName('toggle-button')[0].classList.value.includes('collapsed');
    if (panelCloseState) {
      panelWidth = 0;
    }
  }

  if (scrollTop > minY || southViewPortY < southernmostTileValue) {
    this.graph.container.scrollTop = minY - 20;
  }
  if (scrollLeft > minX || eastViewPortX - panelWidth < easternmostTileValue) {
    this.graph.container.scrollLeft = minX - 120;
  }
},

x축에서 가장 우측에 위치한 타일의 위치와 화면의 오른쪽의 위치를 비교하기 위해
해당 타일의 x좌표 + width 와 스크롤 Left + 화면의 너비 를 비교한 다음,
타일의 좌표가 더 크다면 화면의 왼쪽 위치를 가장 왼쪽의 위치한 타일의 -120 만큼 갖다 놓았다.

같은 매커니즘으로 y축은,
가장 아래에 위치한 타일의 y좌표 + height 와 스크롤 Top + 화면의 높이를 비교해 가장 위쪽에 위치한 타일의 -20 만큼 갖다 놓았다.




fitLocation()을 통과하면
그럼에도 불구하고 화면에 다 들어오지 않는 경우를 체크해서 zoom out을 때리는 로직을 실행시킨다.

// 최남단 타일의 y값이 화면의 y값 보다 크다면 (화면 밖에 있다면);
if (southernmostTileValue > southViewPortY) {
  const startTime = performance.now();
  // Zoom out 한번 때려주고 재귀 태워준다;
  this.zoomOut();
  const endTime = performance.now();
  const elapsedTime = endTime - startTime;
  setTimeout(() => {
    this.fitToView();
  }, elapsedTime + 10);
  // 최동단 타일의 x값이 화면의 x값 보다 크다면;
} else if (easternmostTileValue > eastViewPortX - panelWidth) {
  const startTime = performance.now();
  this.zoomOut();
  const endTime = performance.now();
  const elaspedTime = endTime - startTime;
  setTimeout(() => {
    this.fitToView();
  }, elaspedTime + 10);

줌 아웃을 때린 후 같은 메소드 fitToView()를 호출시켜 재귀를 돌린다.
여기서 타이밍 문제가 발생하여 fitToView를 타지 않는 경우가 있었는데, zoom out 되는 시간을 측정하여 setTimeout으로 zoom out 후 fitToView가 실행되도록 잡아주었다.

profile
Hound on the Code

0개의 댓글