성능 최적화記 (2) - 라인 생성 시 동작

Dzeko·2023년 9월 14일
0

개발일지

목록 보기
101/112

기존 라인(Edge) 생성 시의 동작

  • (라인은 코드에서는 Edge 라는 변수명을 사용하고 있다.)

포트에서 Edge 를 뽑을 때, 포트를 Mouse Move 하는 동작에 mxConnectionHandler 의 mouseMove 메소드가 작동된다.


이 때 Edge가 연결될 수 없는 포트에 투명도(opacity)를 50% 먹임으로써 연결할 수 없는 포트라는 사용자 경험을 선사한다.


또한 다른 Edge에 마우스가 닿았을 때, 마우스가 not-allowed 상태가 되는 사용자 경험도 선사한다.


이 포트의 style을 변경하는 작업을 마우스가 한 픽셀 한 픽셀 움직일 때 마다 하고 있었다.


타일 수가 적을 땐 문제가 없지만, 타일이 많아지고 그에 따라 포트도 늘어나면 굉장히 버벅이고 렉이 걸렸다.


(연결할 수 없는 포트엔 투명도가 적용된다)


(다른 Edge에 닿으면 마우스 커서가 not-allowed가 된다)


1차 개선

처음 개선했던 방법이다.

mouse move할 때마다 스타일을 변경하고 있으므로 매우 비효율적이었다.

어차피 변경된 cell들은 mouse up 될 때까지 그 상태는 유지된다.

mouse move할 때가 아닌, 포트에서 mouse down을 할 때 모두 변경시켜줌으로써 한 번만 작동되게 했다.

2차 개선

그럼에도 문제점이 생겼다.

이제 mouse move할 때는 아무 버벅임이 없었다. 하지만 mouse down 할 때 딜레이가 생다.

많은 수의 포트의 스타일을 변경하느라 바쁜가 보다. 머리가 아파온다

고안해낸 방법은,

mouse down 때 화면에 보이는 포트/Edge 들을 적용시키고,

mouse move 하면서 화면이 옮겨지며 새로 발견되는 포트/Edge를 적용시키기로 했다.


먼저 화면의 포트/Edge를 추출하는 cellsInViewPort라는 메소드를 만들었다.

cellsInViewPort(cells, part) {
    const cellsInView = [];
    const cellsOutOfView = [];
    const {scrollLeft, scrollTop, offsetWidth, offsetHeight} = this.container;

    cells.forEach(cell => {
      const cellBounds = this.view.getState(cell)?.shape.bounds;
      const boundXY = [cellBounds?.x, cellBounds?.y];
      
      if (boundXY[0] > scrollLeft && boundXY[1] > scrollTop && boundXY[0] < scrollLeft + offsetWidth && boundXY[1] < scrollTop + offsetHeight) {
        cellsInView.push(cell);
      } else {
        cellsOutOfView.push(cell);
      }
    });
    const extractedCells = {
      in: cellsInView,
      out: cellsOutOfView,
    }
    if (part === 'in') {
      return extractedCells.in;
    } else if (part === 'out') {
      return extractedCells.out;
    } else {
      return extractedCells;
    }
  }

추출하고싶은 cells 와 viewPort 안의 cell들을 추출할건지 밖의 cell들을 추출할건지 구분하는 'in'/'out' 파라미터를 받고 그에 따라 반환한다.

그리고 connectionHandler의 mouseDown 메소드에,
포트 투명도 작업을 위해 워크플로우의 모든 포트 cells, 'in'을 넣어 화면상의 모든 포트들 받아왔다.
const portsInView = cellsInViewPort(allPorts, 'in');

클릭한 포트는 제외되어야 하기에 제외시켜 준다.
그리고 연결되서는 안되는 포트와 그 id를 추출한다. getInvalidPortsInViewPort라는 메소드를 만들어 추출했다.

const idx = portsInView.findIndex(port => port.id === cell.id);
portsInView.splice(idx, 1);

this.invalidPorts = this.graph.model.getInvalidPortsInViewPort(cell, portsInView);
this.invalidPortsId = new Set(this.invalidPorts.map(port => port.id));

투명도를 적용시키는 메소드는 drawGetOpacityInvalidPorts 이다.

이제 mouse move로 화면을 옮길 때마다 새로운 포트들이 발견되면 넣어줘야 한다.


connectionHandler의 mouseMove 메소드안의 drawPreview라는 메소드에 포트의 id를 체크해서,
invalidPortsId에 없는 포트면 새로 넣어주면서 투명도를 적용시켜줬다.

const cell = this.grabbingPort;
const allPorts = Object.values(this.graph.model.cells).filter(cell => cell?.portType);
const portsInView = this.graph.cellsInViewPort(allPorts, 'in');
const idx = portsInView.findIndex(port => port.id === cell.id);
// 해당 포트 제외;
portsInView.splice(idx, 1);
// 일단 opacity 먹여야하는 포트 구해준다;
const tempInvalidPorts = this.graph.model.getInvalidPortsInViewPort(cell, portsInView);
// 이미 먹여진 애들 제외하고 새로 탐색된 애들;
const toDrawingPorts = [];
tempInvalidPorts.forEach(port => {
   if (!this.invalidPortsId.has(port.id)) {
     toDrawingPorts.push(port);
   }
})

toDrawingPorts?.forEach(port => {
   const portState = this.graph.view.getState(port);
   portState.style.opacity = 50;
   if (portState?.text) {
      portState.style.textOpacity = 50;
      this.graph.cellRenderer.redrawLabel(portState, true);
   }
   portState.shape?.apply(portState);
   portState.shape?.redraw();
   // opacity 먹여주고 invalidPorts에 추가;
   this.invalidPorts.push(port);
   this.invalidPortsId.add(port.id);
})

Edge도 똑같은 방식으로 적용 시켜줬다.

이 작업으로 Edge 생성 시에 4381%, Edge Move 시에 667%의 개선 효율을 얻게 되었다.

profile
Hound on the Code

0개의 댓글