Skip to content

Commit 3a1de82

Browse files
committed
Add 3D cupboard designer component with depth calculation and interactive rotation
1 parent 87cb743 commit 3a1de82

1 file changed

Lines changed: 278 additions & 0 deletions

File tree

components/CupboardDesigner.jsx

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
"use client";
2+
import { useState, useRef, useEffect } from "react";
3+
import styles from "./CupboardDesigner.module.css";
4+
5+
export default function CupboardDesigner() {
6+
const [rotation, setRotation] = useState({ x: 20, y: -25 });
7+
const [depth, setDepth] = useState(40);
8+
const [width, setWidth] = useState(100);
9+
const [height, setHeight] = useState(150);
10+
const [shelves, setShelves] = useState(3);
11+
const [isDragging, setIsDragging] = useState(false);
12+
const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
13+
const containerRef = useRef(null);
14+
const scale = 1.2; // Scale multiplier for display
15+
16+
// Depth calculation for realistic 3D effect
17+
const depthFactor = depth / 100; // Normalized depth (0-1)
18+
19+
// Calculate shelf height based on total height
20+
const shelfHeight = height / (shelves + 0.5);
21+
22+
// Handle mouse drag for rotation
23+
const handleMouseDown = (e) => {
24+
setIsDragging(true);
25+
setDragStart({ x: e.clientX, y: e.clientY });
26+
};
27+
28+
const handleMouseMove = (e) => {
29+
if (!isDragging) return;
30+
31+
const deltaX = e.clientX - dragStart.x;
32+
const deltaY = e.clientY - dragStart.y;
33+
34+
setRotation((prev) => ({
35+
x: Math.max(-90, Math.min(90, prev.x + deltaY * 0.5)),
36+
y: prev.y + deltaX * 0.5,
37+
}));
38+
39+
setDragStart({ x: e.clientX, y: e.clientY });
40+
};
41+
42+
const handleMouseUp = () => {
43+
setIsDragging(false);
44+
};
45+
46+
useEffect(() => {
47+
document.addEventListener("mousemove", handleMouseMove);
48+
document.addEventListener("mouseup", handleMouseUp);
49+
50+
return () => {
51+
document.removeEventListener("mousemove", handleMouseMove);
52+
document.removeEventListener("mouseup", handleMouseUp);
53+
};
54+
}, [isDragging, dragStart]);
55+
56+
// Generate depth gradient stops for 3D effect
57+
const generateDepthGradient = (index, totalShelves) => {
58+
const lightness = 50 + index * (20 / totalShelves); // Lighter towards back
59+
return `hsl(0, 0%, ${lightness}%)`;
60+
};
61+
62+
return (
63+
<div className={styles.container}>
64+
<div className={styles.mainContent}>
65+
{/* 3D Viewer */}
66+
<div
67+
className={styles.viewerContainer}
68+
ref={containerRef}
69+
onMouseDown={handleMouseDown}
70+
style={{
71+
perspective: "1000px",
72+
}}
73+
>
74+
<div
75+
className={styles.cupboardWrapper}
76+
style={{
77+
transform: `rotateX(${rotation.x}deg) rotateY(${rotation.y}deg)`,
78+
transformStyle: "preserve-3d",
79+
transition: isDragging ? "none" : "transform 0.1s ease-out",
80+
}}
81+
>
82+
{/* Cupboard exterior - front face */}
83+
<div
84+
className={styles.cupboardFace}
85+
style={{
86+
width: `${width * scale}px`,
87+
height: `${height * scale}px`,
88+
transform: `translateZ(${(depth / 2) * scale}px)`,
89+
}}
90+
>
91+
<div className={styles.frameOuter} />
92+
93+
{/* Shelves */}
94+
{Array.from({ length: shelves }).map((_, index) => (
95+
<div
96+
key={`shelf-${index}`}
97+
className={styles.shelf}
98+
style={{
99+
top: `${((index + 1) * shelfHeight * scale) / height * 100}%`,
100+
backgroundColor: "#2a2a2a",
101+
boxShadow: `inset 0 -4px 12px rgba(0,0,0,0.8),
102+
inset 0 2px 4px rgba(255,255,255,0.1)`,
103+
height: "8px",
104+
}}
105+
/>
106+
))}
107+
108+
{/* Door */}
109+
<div className={styles.door} />
110+
</div>
111+
112+
{/* Cupboard depth - side faces with gradient */}
113+
{/* Right side */}
114+
<div
115+
className={styles.cupboardSide}
116+
style={{
117+
width: `${depth * scale}px`,
118+
height: `${height * scale}px`,
119+
transform: `rotateY(90deg) translateZ(${(width / 2) * scale}px)`,
120+
background: `linear-gradient(to bottom,
121+
${generateDepthGradient(0, shelves)},
122+
${generateDepthGradient(shelves / 2, shelves)},
123+
${generateDepthGradient(shelves, shelves)})`,
124+
boxShadow: "inset 2px 0 20px rgba(0,0,0,0.6)",
125+
}}
126+
>
127+
{/* Depth shelves visualization */}
128+
{Array.from({ length: shelves }).map((_, index) => (
129+
<div
130+
key={`depth-shelf-${index}`}
131+
className={styles.depthShelf}
132+
style={{
133+
top: `${((index + 1) * shelfHeight * scale) / height * 100}%`,
134+
width: "100%",
135+
height: "2px",
136+
backgroundColor: "rgba(0,0,0,0.3)",
137+
}}
138+
/>
139+
))}
140+
</div>
141+
142+
{/* Left side */}
143+
<div
144+
className={styles.cupboardSide}
145+
style={{
146+
width: `${depth * scale}px`,
147+
height: `${height * scale}px`,
148+
transform: `rotateY(-90deg) translateZ(${(width / 2) * scale}px)`,
149+
background: `linear-gradient(to bottom,
150+
${generateDepthGradient(0, shelves)},
151+
${generateDepthGradient(shelves / 2, shelves)},
152+
${generateDepthGradient(shelves, shelves)})`,
153+
boxShadow: "inset -2px 0 20px rgba(0,0,0,0.6)",
154+
}}
155+
/>
156+
157+
{/* Top */}
158+
<div
159+
className={styles.cupboardTop}
160+
style={{
161+
width: `${width * scale}px`,
162+
height: `${depth * scale}px`,
163+
transform: `rotateX(90deg) translateZ(${(height / 2) * scale}px)`,
164+
background: `linear-gradient(90deg,
165+
rgba(0,0,0,0.4),
166+
rgba(0,0,0,0.2))`,
167+
boxShadow: "inset 0 -3px 15px rgba(0,0,0,0.7)",
168+
}}
169+
/>
170+
171+
{/* Bottom */}
172+
<div
173+
className={styles.cupboardBottom}
174+
style={{
175+
width: `${width * scale}px`,
176+
height: `${depth * scale}px`,
177+
transform: `rotateX(-90deg) translateZ(${(height / 2) * scale}px)`,
178+
background: `linear-gradient(90deg,
179+
rgba(0,0,0,0.5),
180+
rgba(0,0,0,0.3))`,
181+
boxShadow: "inset 0 3px 15px rgba(0,0,0,0.8)",
182+
}}
183+
/>
184+
</div>
185+
</div>
186+
187+
{/* Info Text */}
188+
<p className={styles.infoText}>
189+
💡 Drag to rotate • Mouse wheel to zoom (coming soon)
190+
</p>
191+
</div>
192+
193+
{/* Control Panel */}
194+
<div className={styles.controlPanel}>
195+
<h3 className={styles.panelTitle}>Cupboard Configuration</h3>
196+
197+
{/* Dimensions */}
198+
<div className={styles.controlGroup}>
199+
<label>Width (cm): {width}</label>
200+
<input
201+
type="range"
202+
min="50"
203+
max="200"
204+
value={width}
205+
onChange={(e) => setWidth(Number(e.target.value))}
206+
className={styles.slider}
207+
/>
208+
<span className={styles.unit}>cm</span>
209+
</div>
210+
211+
<div className={styles.controlGroup}>
212+
<label>Height (cm): {height}</label>
213+
<input
214+
type="range"
215+
min="80"
216+
max="250"
217+
value={height}
218+
onChange={(e) => setHeight(Number(e.target.value))}
219+
className={styles.slider}
220+
/>
221+
<span className={styles.unit}>cm</span>
222+
</div>
223+
224+
<div className={styles.controlGroup}>
225+
<label>Depth (cm): {depth}</label>
226+
<input
227+
type="range"
228+
min="20"
229+
max="80"
230+
value={depth}
231+
onChange={(e) => setDepth(Number(e.target.value))}
232+
className={styles.slider}
233+
/>
234+
<span className={styles.unit}>cm</span>
235+
</div>
236+
237+
{/* Shelves */}
238+
<div className={styles.controlGroup}>
239+
<label>Number of Shelves: {shelves}</label>
240+
<input
241+
type="range"
242+
min="1"
243+
max="6"
244+
value={shelves}
245+
onChange={(e) => setShelves(Number(e.target.value))}
246+
className={styles.slider}
247+
/>
248+
</div>
249+
250+
{/* Display Metrics */}
251+
<div className={styles.metrics}>
252+
<h4>Specifications</h4>
253+
<ul>
254+
<li>Internal Volume: {(width * height * depth) / 1000}L</li>
255+
<li>Shelf Capacity: {shelves} shelves</li>
256+
<li>Avg Shelf Space: {((width * depth) / 100).toFixed(2)} cm²</li>
257+
<li>Depth Factor: {depthFactor.toFixed(2)}</li>
258+
</ul>
259+
</div>
260+
261+
{/* Action Buttons */}
262+
<div className={styles.buttonGroup}>
263+
<button
264+
className={styles.button}
265+
onClick={() => {
266+
setRotation({ x: 20, y: -25 });
267+
}}
268+
>
269+
Reset View
270+
</button>
271+
<button className={styles.button + " " + styles.buttonPrimary}>
272+
Save Design
273+
</button>
274+
</div>
275+
</div>
276+
</div>
277+
);
278+
}

0 commit comments

Comments
 (0)