👨‍💻 javascript

[React로 화이트보드] Picking Element Transform issue

읏차 2024. 2. 4. 17:49
pickingElements.forEach((element) => {
        pickingContext.save();
        pickingContext.translate(
          element.translate.x + element.rect.width / 2,
          element.translate.y + element.rect.height / 2
        );
        pickingContext.rotate(15 * (Math.PI / 180));

        pickingContext.translate(
          -element.rect.width / 2,
          -element.rect.height / 2
        );

        const scale = 1.2;
        pickingContext.scale(scale, scale);

        pickingContext.translate(
          element.rect.width / 2,
          element.rect.height / 2
        );

        pickingContext.drawImage(
          element.pickImage,
          element.rect.left,
          element.rect.top,
          element.rect.width,
          element.rect.height,
          -(element.rect.width / 2),
          -(element.rect.height / 2),
          element.rect.width,
          element.rect.height
        );

        pickingContext.restore();
      });​
const updatePickingCanvas = () => {
    if (pickingContext) {
      pickingElements.forEach((element) => {
        pickingContext.drawImage(
          element.pickImage,
          element.rect.left,
          element.rect.top,
          element.rect.width,
          element.rect.height,
          element.translate.x,
          element.translate.y,
          element.rect.width,
          element.rect.height
        );
      });
    }
  };

 

먼저 위치값에 대한 변환을 적용하려했다.

 

[버그] 이동 전
[버그] 이동 후

 

현재 하나의 Picking Canvas에서
1. 새로운 picking Element를 그려주는 작업

2. 다 그려진 element의 영역을 이미지로 변환하여 picking Elements로 추가하고 elements를 Canvas에 그려주는 작업

이 두 작업을 하고 있어서 발생한 문제였다.

        <div className="canvas_container">
          <canvas className="picking_canvas" ref={pickingDrawCanvas} />
          <canvas className="main_canvas" ref={mainCanvas} />
          <canvas className="picking_canvas" ref={pickingCanvas} />
        </div>

 

1번의 작업을 위해 pickingDrawCanvas를 하나 더 만들어줘서 element List에 추가하기 전에 현재 그려지고 있는 element만을 위한 렌더링 작업을 하도록 변경하였다.

 

[수정] 이동 전
[수정] 이동 후

 

이제 translate, rotate, scale 처리를 해줘야한다. 먼저 translate와 rotate 처리를 해주자

	pickingContext.save();
        pickingContext.translate(
          element.translate.x + element.rect.width / 2,
          element.translate.y + element.rect.height / 2
        );
        pickingContext.rotate(15 * (Math.PI / 180));
        
        pickingContext.drawImage(
          element.pickImage,
          element.rect.left,
          element.rect.top,
          element.rect.width,
          element.rect.height,
          -(element.rect.width / 2),
          -(element.rect.height / 2),
          element.rect.width,
          element.rect.height
        );
        pickingContext.restore();

먼저 도형의 정가운데 원점으로 translate 이동해준다. 이렇게 해야 원점을 중심으로 Rotate하게 된다.

그리고 DrawImage 함수에서
기존에 translate(0, 0)일때는 이미지의 시작점 (top,left)이 translate.x y만큼 이동한 위치

현재는 translate(x+width/2, y+height/2)만큼 적용되어 있으므로 이미지의 시작점(top, left)는 -width/2, height/2가 된다.

 

여기까지 했으면 translate와 rotate에 대해서는 문제없이 동작한다.

scale 버튼들

현재 코드에서 rotate 아래에 scale을 추가하면 원점을 중심으로 크기가 변경된다.

하지만 그림처럼 각각의 크기 조정 버튼을 클릭하고 scale변경 시 원점이 중심이 아닌 각각 다른 곳으로 설정해줘야 알맞은 동작을 하게 된다.

 

     pickingElements.forEach((element) => {
        pickingContext.save();
        pickingContext.translate(
          element.translate.x + element.rect.width / 2,
          element.translate.y + element.rect.height / 2
        );
        pickingContext.rotate(15 * (Math.PI / 180));

        pickingContext.translate(
          -element.rect.width / 2,
          -element.rect.height / 2
        );

        const scale = 1.2;
        pickingContext.scale(scale, scale);

        pickingContext.translate(
          element.rect.width / 2,
          element.rect.height / 2
        );

        pickingContext.drawImage(
          element.pickImage,
          element.rect.left,
          element.rect.top,
          element.rect.width,
          element.rect.height,
          -(element.rect.width / 2),
          -(element.rect.height / 2),
          element.rect.width,
          element.rect.height
        );

        pickingContext.restore();
      });

 

 

context가 아닌 element.style.transform을 위 코드처럼 변경해야하는 경우도 있는데 다음과 같이 변경해주면 된다.

const translate = `translate(${el.rect.left}px, ${el.rect.top}px)`;
const rotate = `rotate(${15}deg)`;
const translateOrigin = `translate(${-el.rect.width / 2}px, ${-el.rect.height / 2}px)`;
const scale = `scale(${1.2}, ${1.2})`;
const translateRevert = `translate(${el.rect.width / 2}px, ${el.rect.height / 2}px)`;

canvasRef.current.style.transform = `${translate} ${rotate} ${translateOrigin} ${scale} ${translateRevert}`;