평면도엔 그리드 배경이 필요하다. 눈금 있어야 배치하기 편함.

첫 시도: 전부 그리기
단순하게 생각했다.
const lines = [];
for (let x = 0; x < 10000; x += 10) {
lines.push({ points: [x, 0, x, 10000] });
}
for (let y = 0; y < 10000; y += 10) {
lines.push({ points: [0, y, 10000, y] });
}
10px 간격으로 선 2000개.
렉 걸림. 줌/팬할 때마다 버벅거린다.
해결: viewport만 그리기
화면에 보이는 영역만 그리면 된다.
function getVisibleArea(stage: Konva.Stage) {
const transform = stage.getAbsoluteTransform().copy().invert();
const topLeft = transform.point({ x: 0, y: 0 });
const bottomRight = transform.point({
x: stage.width(),
y: stage.height()
});
return { topLeft, bottomRight };
}
그리드 그리기
function generateGridLines(area: VisibleArea, spacing: number) {
const lines = [];
// 시작점을 spacing 배수로 맞춤
const startX = Math.floor(area.topLeft.x / spacing) * spacing;
const startY = Math.floor(area.topLeft.y / spacing) * spacing;
// 세로선
for (let x = startX; x <= area.bottomRight.x; x += spacing) {
lines.push({
points: [x, area.topLeft.y, x, area.bottomRight.y],
stroke: '#ddd',
strokeWidth: 1
});
}
// 가로선
for (let y = startY; y <= area.bottomRight.y; y += spacing) {
lines.push({
points: [area.topLeft.x, y, area.bottomRight.x, y],
stroke: '#ddd',
strokeWidth: 1
});
}
return lines;
}
이제 화면에 보이는 선만 그린다. 수십 개 정도.
줌 레벨별 간격
줌 아웃하면 선이 너무 빽빽해진다.
function getGridSpacing(scale: number) {
if (scale > 1.5) return 10; // 줌 인: 10px
if (scale > 0.5) return 50; // 보통: 50px
return 100; // 줌 아웃: 100px
}
메인 그리드 강조
50px마다 진한 선:
const isMainLine = (pos: number) => pos % 50 === 0;
lines.push({
points: [...],
stroke: isMainLine(x) ? '#bbb' : '#eee',
strokeWidth: isMainLine(x) ? 1 : 0.5
});
성능 팁
그리드는 자주 안 바뀌니까 별도 Layer에 두고 캐싱:
gridLayer.cache();
줌/팬할 때만 다시 그리면 됨.
다음 글에서 줌/팬 구현.