javascript
Web Workers와 OffscreenCanvas로 canvas 성능 향상
hjcode
2024. 10. 21. 13:27
이번 프로젝트에서 구현해야하는 기능은 이미지 위에 겹쳐진 투명한 캔버스에 그림을 그릴 수 있는 웹 기반 그리기 도구를 만드는 것이었습니다. 그런데 캔버스 사이즈가 커지고 그리는 모양 수가 늘어나면서, 특히 자유형 그리기 중에 상당한 지연이 발생하기 시작했습니다.
여기서 몇몇 최적화 기술을 시도했습니다.
- 디바운싱 및 스로틀링: 이러한 기술은 마우스 이벤트 호출 수를 줄이는 데 도움이 되었지만 그리기의 부드러움도 감소시켰습니다(ux관점에서 안좋음).
- 단순화된 그리기 알고리즘: 그리기 알고리즘을 최적화했지만, 미미한 개선만 제공했습니다.
주요 문제는 모든 그리기 작업이 UI 업데이트 및 이벤트 처리와 경쟁하면서 메인 스레드에서 수행된다는 것이였고, 이러한 생각으로 인해 그리기 작업을 별도의 스레드로 오프로드하는 것을 찾게 되었습니다.
웹 워커를 OffscreenCanvas와 함께 사용하면 메인 스레드의 성능에 영향을 미치지 않고 그리기 작업을 수행할 수 있었습니다.
Web Workers와 OffscreenCanvas
- Web Workers: 웹 애플리케이션의 주 실행 스레드와 별도로 백그라운드 스레드에서 스크립트를 실행할 수 있습니다.
- OffscreenCanvas: 화면 밖에서 렌더링할 수 있는 캔버스를 제공하여 메인 스레드에 영향을 주지 않고도 그리기 작업을 수행할 수 있습니다.
기본 구현
메인 스레드
// 메인 스레드
const canvas = document.getElementById('myCanvas');
const offscreen = canvas.transferControlToOffscreen();
const worker = new Worker('drawing-worker.js');
worker.postMessage({ canvas: offscreen }, [offscreen]);
Web Worker(worker.js)
let canvas, ctx;
self.onmessage = function(evt) {
if (evt.data.canvas) {
canvas = evt.data.canvas;
ctx = canvas.getContext('2d');
} else if (evt.data.drawCommand) {
// 그리기 명령 처리
performDrawing(evt.data.drawCommand);
}
};
function performDrawing(command) {
// 그리기 구현
// 예시:
ctx.beginPath();
ctx.moveTo(command.startX, command.startY);
ctx.lineTo(command.endX, command.endY);
ctx.stroke();
}
메인 스레드에서 그리기 명령 보내기
// 메인 스레드
canvas.addEventListener('mousemove', (evt) => {
if (isDrawing) {
worker.postMessage({
drawCommand: {
startX: lastX,
startY: lastY,
endX: evt.clientX,
endY: evt.clientY
}
});
lastX = evt.clientX;
lastY = evt.clientY;
}
});
웹 워커와 OffscreenCanvas를 사용하면 캔버스의 성능을 크게 높일 수 있습니다.
집중적인 드로잉 작업을 메인 스레드에서 벗어나게 하면 복잡한 드로잉이나 큰 캔버스를 처리할 때에도
더 매끄럽고 반응성이 뛰어난 사용자 경험을 만들 수 있습니다.
메인 스레드와 웹 워커 간에 메시지를 전달하는 데 약간의 오버헤드가 있으므로 단순한 형태라면 도입시 고려해볼 부분입니다.
https://developer.mozilla.org/ko/docs/Web/API/Web_Workers_API
https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
반응형