이제 가구를 배치해보자.

가구 타입 정의
interface Furniture {
id: string;
type: FurnitureType;
name: string;
x: number;
y: number;
width: number;
height: number;
rotation: number;
color: string;
shape: 'rect' | 'circle' | 'ellipse' | 'lshape';
}
type FurnitureType =
| 'bed'
| 'desk'
| 'chair'
| 'sofa'
| 'table'
| 'wardrobe'
| 'bookshelf'
| 'tv'
| 'refrigerator'
| 'washingMachine';
10종류. 더 추가할 수 있지만 일단 이 정도.
기본값
가구마다 기본 크기가 다르다:
const FURNITURE_DEFAULTS: Record<FurnitureType, Partial<Furniture>> = {
bed: { width: 200, height: 150, color: '#8B4513' },
desk: { width: 120, height: 60, color: '#D2691E' },
chair: { width: 50, height: 50, color: '#A0522D' },
sofa: { width: 200, height: 80, color: '#708090' },
table: { width: 150, height: 90, color: '#DEB887' },
wardrobe: { width: 120, height: 60, color: '#8B0000' },
bookshelf: { width: 100, height: 30, color: '#CD853F' },
tv: { width: 120, height: 10, color: '#2F4F4F' },
refrigerator: { width: 70, height: 70, color: '#C0C0C0' },
washingMachine: { width: 60, height: 60, color: '#E0E0E0' }
};
침대는 200x150cm, 의자는 50x50cm.
드래그 앤 드롭
팔레트에서 캔버스로 드래그.
HTML5 DnD vs Konva DnD
처음엔 HTML5 드래그 앤 드롭 썼다.
<div draggable="true" @dragstart="onDragStart">
침대
</div>
근데 문제: 캔버스에 drop할 때 좌표 계산이 복잡함.
해결: 클릭으로 변경
그냥 팔레트 클릭하면 캔버스 중앙에 배치되게 바꿨다.
function addFurniture(type: FurnitureType) {
const defaults = FURNITURE_DEFAULTS[type];
const center = getViewportCenter();
const furniture: Furniture = {
id: generateId(),
type,
name: type,
x: center.x - defaults.width / 2,
y: center.y - defaults.height / 2,
...defaults
};
store.addFurniture(furniture);
}
배치 후 드래그해서 위치 조정하면 됨.
Pinia 스토어
export const useFurnitureStore = defineStore('furniture', () => {
const items = ref<Furniture[]>([]);
const selectedId = ref<string | null>(null);
function addFurniture(furniture: Furniture) {
items.value.push(furniture);
selectedId.value = furniture.id;
}
function updateFurniture(id: string, updates: Partial<Furniture>) {
const item = items.value.find(f => f.id === id);
if (item) {
Object.assign(item, updates);
}
}
function deleteFurniture(id: string) {
const index = items.value.findIndex(f => f.id === id);
if (index !== -1) {
items.value.splice(index, 1);
if (selectedId.value === id) {
selectedId.value = null;
}
}
}
return { items, selectedId, addFurniture, updateFurniture, deleteFurniture };
});
캔버스에 렌더링
<v-layer>
<template v-for="furniture in store.items" :key="furniture.id">
<FurnitureShape
:config="furniture"
@click="selectFurniture(furniture.id)"
@dragend="onDragEnd"
/>
</template>
</v-layer>
FurnitureShape 컴포넌트에서 shape에 따라 다르게 렌더링.
다음 글에서 다양한 모양 구현.